Skip to content

Instantly share code, notes, and snippets.

@kingbuzzman
Last active June 2, 2019 08:24
Show Gist options
  • Save kingbuzzman/05ed095d8f48c3904e217e56235af54a to your computer and use it in GitHub Desktop.
Save kingbuzzman/05ed095d8f48c3904e217e56235af54a to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Stolen from: https://mlvin.xyz/django-single-file-project.html
import inspect
import os
import sys
from types import ModuleType
import django
from django.conf import settings
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# The current name of the file, which will be the name of our app
APP_LABEL, _ = os.path.splitext(os.path.basename(os.path.abspath(__file__)))
# Migrations folder need to be created, and django needs to be told where it is
APP_MIGRATION_MODULE = '%s_migrations' % APP_LABEL
APP_MIGRATION_PATH = os.path.join(BASE_DIR, APP_MIGRATION_MODULE)
# Create the folder and a __init__.py if they don't exist
if not os.path.exists(APP_MIGRATION_PATH):
os.makedirs(APP_MIGRATION_PATH)
open(os.path.join(APP_MIGRATION_PATH, '__init__.py'), 'w').close()
# Hack to trick Django into thinking this file is actually a package
sys.modules[APP_LABEL] = sys.modules[__name__]
sys.modules[APP_LABEL].__path__ = [os.path.abspath(__file__)]
settings.configure(
DEBUG=True,
ROOT_URLCONF='%s.urls' % APP_LABEL,
MIDDLEWARE=(
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
),
INSTALLED_APPS=[
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.staticfiles',
APP_LABEL,
],
MIGRATION_MODULES={APP_LABEL: APP_MIGRATION_MODULE},
SITE_ID=1,
DATABASES={
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
},
LOGGING={
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'simple': {
'format': "%(levelname)s %(message)s",
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple',
}
},
'loggers': {
'django.db.backends': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': False},
'django.db.backends.schema': {'level': 'ERROR'}, # Causes sql logs to duplicate -- really annoying
}
},
STATIC_URL='/static/'
)
django.setup()
from django.apps import apps # noqa: E402 isort:skip
# Setup the AppConfig so we don't have to add the app_label to all our models
def get_containing_app_config(module):
if module == '__main__':
return apps.get_app_config(APP_LABEL)
return apps._get_containing_app_config(module)
apps._get_containing_app_config = apps.get_containing_app_config
apps.get_containing_app_config = get_containing_app_config
# Your code below this line
# ##############################################################################
from django.db import models # noqa: E402 isort:skip
from django.test import TestCase # noqa: E402 isort:skip
from django.contrib.auth import get_user_model # noqa: E402 isort:skip
User = get_user_model()
class Product(models.Model):
# category = models.ForeignKey(Category, on_delete=models.CASCADE) # TODO: uncomment, didnt want to model this out
name = models.CharField(max_length=200, db_index=True)
slug = models.SlugField(max_length=200, db_index=True)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2) # Not used FloatField to avoid rounding issues # this is correct, no need to explain this, anyonw that works with django, gets this.
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
contact = models.BigIntegerField(default=None, blank=True, null=True)
created_by = models.CharField(max_length=200, default=None, blank=True, null=True)
uploaded_by_id = models.IntegerField(default=0) # TODO: use ForeignKey(User) here!!!
status = models.IntegerField(default=0) # 0-->Active,1-->Inactive # TODO: learn to use `choices`
mark_as_sold = models.IntegerField(default=0) # 0-->not sold,1-->sold # TODO: there is something called `BooleanField` use it!
class Meta:
ordering = ('-created',)
index_together = (('id', 'slug'),) # we want to query product by id and slug using together index to improve performance
def get_absolute_url(self):
return reverse('olx:edit_product', kwargs={'pk': self.pk})
def __str__(self):
return self.name
class Photo(models.Model):
product = models.ForeignKey(Product, null=True, on_delete=models.CASCADE, db_column='reference_id')
photo_type = models.CharField(max_length=70, db_index=True)
file = models.FileField(upload_to='photos/', default='NoImage.jpg')
cover_photo_flag = models.CharField(default=0, max_length=5, db_index=True) # TODO: learn to use `choices`, and you use "yes" / "no" -- and the default is 0 -- FIX THIS!!
uploaded_at = models.DateTimeField(auto_now_add=True)
uploaded_by_id = models.IntegerField(default=0) # TODO: use ForeignKey(User) here!!!
status = models.IntegerField(default=0) # 0-->Active,1-->Inactive # TODO: learn to use `choices` -- perhaps just call this "is_active" and make it a bool
class Meta:
ordering = ('-uploaded_at',)
class ProductLikeDislike(models.Model):
product = models.ForeignKey(Product, models.SET_DEFAULT, default=0) # TODO: default=0?? this is pretty bad. models.ForeignKey(Product, models.SET_NULL, null=True) is much better
product_liked_by = models.ForeignKey(User, models.SET_DEFAULT, default=0, db_column='product_liked_by_id') # TODO: default=0?? this is pretty bad. models.ForeignKey(ForeignKey, models.SET_NULL, null=True) is much better
status = models.BooleanField(default=False) # TODO: rename, bad name. try something like "liked" / "disliked" OR go with IntegerField(choices=((0, 'Liked'), (1, 'Disliked')) if you have more than 2 values.
urlpatterns = []
class ModelQueryTestCase(TestCase):
def setUp(self):
user1 = User.objects.create(username='user1')
user2 = User.objects.create(username='user2')
user3 = User.objects.create(username='user3')
p1 = Product.objects.create(name='product1', slug='', description='', price=100)
p2 = Product.objects.create(name='product2', slug='', description='', price=100)
p3 = Product.objects.create(name='product3', slug='', description='', price=100)
ProductLikeDislike.objects.create(product_liked_by=user1, product=p1, status=True)
ProductLikeDislike.objects.create(product_liked_by=user2, product=p1, status=True)
ProductLikeDislike.objects.create(product_liked_by=user2, product=p3, status=True)
Photo.objects.create(cover_photo_flag='yes', product=p1, status=False, file='p1_main.png')
Photo.objects.create(cover_photo_flag='no', product=p1, status=False, file='p1_sec.png')
Photo.objects.create(cover_photo_flag='yes', product=p2, status=False, file='p2_main.png')
Photo.objects.create(cover_photo_flag='no', product=p2, status=False, file='p2_sec.png')
def test_user1_likes(self):
from django.db import models # needs to be here becuase of the hack that is having a single django file # noqa
user_i_care_about = User.objects.get(username='user2')
productlikedislike_set = models.Prefetch('productlikedislike_set',
ProductLikeDislike.objects.select_related('product_liked_by')
.filter(product_liked_by=user_i_care_about) \
.order_by('id'))
photo_set = models.Prefetch('photo_set', Photo.objects.order_by('id'))
products = Product.objects.prefetch_related(photo_set, productlikedislike_set) \
.filter(models.Q(photo__cover_photo_flag='yes') | models.Q(photo__isnull=True)) \
.filter(productlikedislike__product_liked_by=user_i_care_about).order_by('id')
self.assertEqual(len(products), 2)
self.assertEqual(products[0].name, 'product1')
self.assertEqual([x.file.name for x in products[0].photo_set.all()], ['p1_main.png', 'p1_sec.png'])
self.assertEqual(products[1].name, 'product3')
self.assertEqual([x.file.name for x in products[1].photo_set.all()], [])
self.assertEqual([x.status for x in products[0].productlikedislike_set.all()], [True])
self.assertEqual([x.status for x in products[1].productlikedislike_set.all()], [True])
# Your code above this line
# ##############################################################################
# Used so you can do 'from <name of file>.models import *'
models_module = ModuleType('%s.models' % (APP_LABEL))
tests_module = ModuleType('%s.tests' % (APP_LABEL))
urls_module = ModuleType('%s.urls' % (APP_LABEL))
urls_module.urlpatterns = urlpatterns
for variable_name, value in list(locals().items()):
# We are only interested in models
if inspect.isclass(value) and issubclass(value, models.Model):
setattr(models_module, variable_name, value)
# We are only interested in tests
if inspect.isclass(value) and issubclass(value, TestCase):
setattr(tests_module, variable_name, value)
# Setup the fake modules
sys.modules[models_module.__name__] = models_module
sys.modules[tests_module.__name__] = tests_module
sys.modules[urls_module.__name__] = urls_module
sys.modules[APP_LABEL].models = models_module
sys.modules[APP_LABEL].tests = tests_module
sys.modules[APP_LABEL].urls = urls_module
if __name__ == "__main__":
# Hack to fix tests
argv = [arg for arg in sys.argv if not arg.startswith('-')]
if len(argv) == 2 and argv[1] == 'test':
sys.argv.append(APP_LABEL)
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
else:
from django.core.wsgi import get_wsgi_application
get_wsgi_application()
@kingbuzzman
Copy link
Author

In order to run the code above run the following:

curl -L https://gist.githubusercontent.com/kingbuzzman/05ed095d8f48c3904e217e56235af54a/raw/otc.py > otc.py
pip install django==2.1 
python otc.py makemigrations
python otc.py test

If you want the REAL copy and paste version:

docker run -it --rm python:3.7 bash -c '
curl -L https://gist.githubusercontent.com/kingbuzzman/05ed095d8f48c3904e217e56235af54a/raw/otc.py > otc.py
pip install django==2.1 
python otc.py makemigrations
python otc.py test
'

Answers stackoverflow question: https://stackoverflow.com/questions/56406182/django-raw-query-giving-same-result-on-all-models/56407187#56407187

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment