Skip to content

Instantly share code, notes, and snippets.

@kayode-adechinan
Last active March 26, 2020 11:00
Show Gist options
  • Save kayode-adechinan/cbe33cf32d167ec2bb61e4a4b32b698c to your computer and use it in GitHub Desktop.
Save kayode-adechinan/cbe33cf32d167ec2bb61e4a4b32b698c to your computer and use it in GitHub Desktop.
Django Cheat Sheet

Random string - random username

from django.utils.crypto import get_random_string
User.objects.create_user(username=get_random_string(), email='', password='123')

Build API

install

$ mkdir api & cd api
$ pipenv install django djangorestframework django-cors-headers django-filter django-extensions
$ django-admin startproject api . & python manage.py startapp core

steps

# in settings.py
ALLOWED_HOSTS = ['*']

# in INSTALLED_APPS
'django_extensions',
'rest_framework',
'django_filters',
'corsheaders',
'core.apps.CoreConfig',

# IN MIDDLEWARE SECTION
'corsheaders.middleware.CorsMiddleware',

# CORS CONFIG
CORS_ORIGIN_ALLOW_ALL = True

# FILTER AND PAGINATION SECTION
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 24,
     'DEFAULT_FILTER_BACKENDS': (
         'django_filters.rest_framework.DjangoFilterBackend',
    ),
}

# models.py
class Product(models.Model):
    name = models.CharField(max_length=200, db_index=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)

# serializers.py
from rest_framework import serializers
from search import models
class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        fields = (
            'id',
            'name',
            'price',
        )
        model = models.Product

# filters.py
import django_filters
from search import models

class ProductFilter(django_filters.FilterSet):
      near_to= filters.CharFilter(method='get_nearest')
      
      class Meta:
          model = models.Product
          fields = {
               'name': ['exact', 'icontains'],
               'price': ['exact', 'lte', 'gte', 'range'],
          }
      
      def get_nearest(self, queryset, name, value):
          return queryset.filter(location__distance_lte=(user_location, D(km=7)))



# views.py
from rest_framework import viewsets
from search import models
from search import serializers
from search import filters

# Create your views here.
class ProductViewSet(viewsets.ModelViewSet):
    queryset = models.Product.objects.all()
    serializer_class = serializers.ProductSerializer
    filterset_class = filters.ProductFilter
    ordering_fields = ['username', 'email'] # ?ordering=-username # ?ordering=account,username
 
# core/urls
from django.urls import path
from search import views
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('products', views.ProductViewSet)

urlpatterns = router.urls

# api/urls
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('search.urls'))
]

Permission

from rest_framework.permissions import BasePermission,SAFE_METHODS

class IsOwnerProfileOrReadOnly(BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in SAFE_METHODS:
            return True
        return obj.user==request.user


from rest_framework import viewsets, permissions
class ItemViewSet(viewsets.ModelViewSet):
    
    permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerProfileOrReadOnly ]

Authentication

# pipenv install djangorestframework-simplejwt

# settings.py
REST_FRAMEWORK = {
    # ...

    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    )

}


CORS_ORIGIN_ALLOW_ALL = True


from datetime import timedelta

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=50),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
}

AUTH_USER_MODEL = 'account.User'


AUTHENTICATION_BACKENDS = (
  'account.custom_auth_backends.EmailAuthBackend',
  'django.contrib.auth.backends.ModelBackend',
)

# custom_auth_backends.py
from account import models
from django.contrib.auth.hashers import check_password


class EmailAuthBackend(object):
    def authenticate(self, request, username=None, password=None):
        try:
      # Check if the user exists in Django's database
            user = models.User.objects.get(email=username)
        except models.User.DoesNotExist:
            return None

    # Check password of the user we found
        if check_password(password, user.password):
            return user
        return None
    # Required for your backend to work properly - unchanged in most scenarios
    def get_user(self, user_id):
        try:
            return models.User.objects.get(pk=user_id)
        except models.User.DoesNotExist:
            return None

#models.py
from django.db import models

# Create your models here.
from django.db import models
from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    USER_TYPE_CHOICES = (
          (1, 'student'),
          (2, 'teacher'),
          (3, 'secretary'),
          (4, 'supervisor'),
          (5, 'admin'),
      )
    user_type = models.PositiveSmallIntegerField(choices=USER_TYPE_CHOICES)
    email = models.EmailField(verbose_name='email address', max_length=255, unique=True)
    phone = models.CharField(max_length=30)
    def __str__(self):
        return self.email
        
# serializers.py
from account.models import User
from rest_framework import serializers
from django.contrib.auth import authenticate


class SignUpSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'first_name', 'last_name', 'email', 'password',)
        extra_kwargs = {'password': {'write_only': True}}

    def create(self, validated_data):
        user = User.objects.create_user(
                                        first_name= validated_data['first_name'],
                                        last_name= validated_data['last_name'],
                                        username=validated_data['first_name'],
                                        email=validated_data['email'],
                                        password=validated_data['password'])
        return user



class SignInSerializer(serializers.Serializer):
    email = serializers.CharField()
    password = serializers.CharField()

    def validate(self, data):
        user = authenticate(username=data['email'], password=data['password'])
        if user is not None:
            return user
        raise serializers.ValidationError("Unable to log in with provided credentials.")



class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'email', 'first_name', 'last_name', 'username')        


# views.py
from rest_framework import viewsets, permissions, generics
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from account import serializers
from rest_framework import views, permissions, status
from rest_framework.response import Response
from account import models
from account import filters


class SignUpAPI(generics.GenericAPIView):
    serializer_class = serializers.SignUpSerializer
    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()
        refresh = RefreshToken.for_user(user)
        return Response({
            'user': serializers.UserSerializer(user, context=self.get_serializer_context()).data,
            'refresh': str(refresh),
            'access': str(refresh.access_token),
        })



class SignInAPI(generics.GenericAPIView):
    serializer_class = serializers.SignInSerializer

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data
        refresh = RefreshToken.for_user(user)
        return Response({
            'user': serializers.UserSerializer(user, context=self.get_serializer_context()).data,
            'refresh': str(refresh),
            'access': str(refresh.access_token),
        })



class UserAPI(generics.RetrieveAPIView):
    permission_classes = [permissions.IsAuthenticated, ]
    serializer_class = serializers.UserSerializer
    def get_object(self):
        return self.request.user



class UserViewSet(viewsets.ModelViewSet):
    queryset = models.User.objects.all()
    serializer_class = serializers.UserSerializer
    filterset_class = filters.UserFilter

# urls.py
# ...
urlpatterns=[
    path('auth/users/me/', views.UserAPI.as_view()),
    path('auth/sign-up/', views.SignUpAPI.as_view()),
    path('auth/sign-in/', views.SignInAPI.as_view()),
    path('auth/refresh-token/', TokenRefreshView.as_view()),
]
# ...

Custom command

  • create management/commands folder inside an app folder
  • add this inside a fixture.py
from django.core.management.base import BaseCommand
from search import models
from random import randrange

class Command(BaseCommand):
    help = "fake data generator"

    def handle(self, *args, **options):

        models.Product.objects.all().delete()


        for i in range(400):
            models.Product.objects.create(
                name="Product "+ str(i),
                price=randrange(97562),
            )

        self.stdout.write(self.style.SUCCESS("done!"))

Custom command with param

from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = "fake data generator"

    def add_arguments(self, parser):
        # Required argument
        parser.add_argument('total', type=int, help='Indicates the number of item to be created')

        # Optional argument
        parser.add_argument('-p', '--price', type=str, help='Define an item price', )

        # Optional list argument
        parser.add_argument('-i', '--items', nargs='+', type=int, help='Item ID')



    def handle(self, *args, **options):
        total = options['total']
        price = options['price']
        items = options['items']



        self.stdout.write(self.style.SUCCESS('total "%s"' % total))
        if price:
            self.stdout.write(self.style.SUCCESS('price "%s"' % price))
        if items:
            self.stdout.write(self.style.SUCCESS('items "%s"' % items))

Models

Inheritance

One to one

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

    def __str__(self):
        return "%s the place" % self.name

class Restaurant(models.Model):
    place = models.OneToOneField(
        Place,
        on_delete=models.CASCADE,
        primary_key=True,
    )
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

    def __str__(self):
        return "%s the restaurant" % self.place.name

class Waiter(models.Model):
    restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
    name = models.CharField(max_length=50)

    def __str__(self):
        return "%s the waiter at %s" % (self.name, self.restaurant)

Many to one

class Reporter(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    email = models.EmailField()

    def __str__(self):
        return "%s %s" % (self.first_name, self.last_name)

class Article(models.Model):
    headline = models.CharField(max_length=100)
    pub_date = models.DateField()
    reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)

    def __str__(self):
        return self.headline

    class Meta:
        ordering = ['headline']

Many to may

class Publication(models.Model):
    title = models.CharField(max_length=30)

    class Meta:
        ordering = ['title']

    def __str__(self):
        return self.title

class Article(models.Model):
    headline = models.CharField(max_length=100)
    publications = models.ManyToManyField(Publication)

    class Meta:
        ordering = ['headline']

    def __str__(self):
        return self.headline

Many to many through

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __str__(self):
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

Model inheritance

class Media(models.Model):
    title = models.CharField(max_length=255)
    pub_date = models.DateTimeField()

class Photo(Media): # note that Photo extends Media
    image = models.ImageField(upload_to='photos')

class Video(Media):
    video = models.FileField(upload_to='videos')

Invoice

class Invoice(models.Model):
    SENT = 1
    PAID = 2
    VOID = 3
    STATUS_CHOICES = (
        (SENT, 'sent'),
        (PAID, 'paid'),
        (VOID, 'void'),
    )

    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='invoices')
    number = models.CharField(max_length=30)
    date = models.DateTimeField(auto_now_add=True)
    status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES)
    amount = models.DecimalField(max_digits=10, decimal_places=2)

User

class User(AbstractUser):
  USER_TYPE_CHOICES = (
      (1, 'student'),
      (2, 'teacher'),
      (3, 'secretary'),
      (4, 'supervisor'),
      (5, 'admin'),
  )

  user_type = models.PositiveSmallIntegerField(choices=USER_TYPE_CHOICES)

Product | Ecommerce | Shop

class Product(models.Model):
    category = models.ForeignKey(Category,
                                 related_name='products',
                                 on_delete=models.CASCADE)
    name = models.CharField(max_length=200, db_index=True)
    slug = models.SlugField(max_length=200, db_index=True)
    image = models.ImageField(upload_to='products/%Y/%m/%d',
                              blank=True)
    description = models.TextField(blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    available = models.BooleanField(default=True)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ('name',)
        index_together = (('id', 'slug'),)

    def __str__(self):
        return self.name

    def get_absolute_url(self):
            return reverse('shop:product_detail',
                           args=[self.id, self.slug])
                           
                           
class Order(models.Model):
    first_name = models.CharField(_('first name'), max_length=50)
    last_name = models.CharField(_('last name'), max_length=50)
    email = models.EmailField(_('e-mail'))
    address = models.CharField(_('address'), max_length=250)
    postal_code = models.CharField(_('postal code'), max_length=20)
    city = models.CharField(_('city'), max_length=100)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    paid = models.BooleanField(default=False)
    braintree_id = models.CharField(max_length=150, blank=True)
    coupon = models.ForeignKey(Coupon,
                               related_name='orders',
                               null=True,
                               blank=True,
                               on_delete=models.SET_NULL)
    discount = models.IntegerField(default=0,
                                   validators=[MinValueValidator(0),
                                               MaxValueValidator(100)])

    class Meta:
        ordering = ('-created',)

    def __str__(self):
        return 'Order {}'.format(self.id)

    def get_total_cost(self):
        total_cost = sum(item.get_cost() for item in self.items.all())
        return total_cost - total_cost * (self.discount / Decimal('100'))


class OrderItem(models.Model):
    order = models.ForeignKey(Order,
                              related_name='items',
                              on_delete=models.CASCADE)
    product = models.ForeignKey(Product,
                                related_name='order_items',
                                on_delete=models.CASCADE)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    quantity = models.PositiveIntegerField(default=1)

    def __str__(self):
        return '{}'.format(self.id)

    def get_cost(self):
        return self.price * self.quantity                           

Post | Comment

class Post(models.Model): 
    STATUS_CHOICES = ( 
        ('draft', 'Draft'), 
        ('published', 'Published'), 
    ) 
    title = models.CharField(max_length=250) 
    slug = models.SlugField(max_length=250,  
                            unique_for_date='publish') 
    author = models.ForeignKey(User, 
                               on_delete=models.CASCADE,
                               related_name='blog_posts') 
    body = models.TextField() 
    publish = models.DateTimeField(default=timezone.now) 
    created = models.DateTimeField(auto_now_add=True) 
    updated = models.DateTimeField(auto_now=True) 
    status = models.CharField(max_length=10,  
                              choices=STATUS_CHOICES, 
                              default='draft') 
    
    objects = models.Manager() # The default manager. 
    published = PublishedManager() # Our custom manager.
    tags = TaggableManager()

    class Meta: 
        ordering = ('-publish',) 

    def __str__(self): 
        return self.title

    def get_absolute_url(self):
        return reverse('blog:post_detail',
                       args=[self.publish.year,
                             self.publish.month,
                             self.publish.day,
                             self.slug])


class Comment(models.Model): 
    post = models.ForeignKey(Post,
                             on_delete=models.CASCADE,
                             related_name='comments')
    name = models.CharField(max_length=80) 
    email = models.EmailField() 
    body = models.TextField() 
    created = models.DateTimeField(auto_now_add=True) 
    updated = models.DateTimeField(auto_now=True) 
    active = models.BooleanField(default=True) 
 
    class Meta: 
        ordering = ('created',) 
 
    def __str__(self): 
        return 'Comment by {} on {}'.format(self.name, self.post)

Course

class Subject(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True)

    class Meta:
        ordering = ['title']

    def __str__(self):
        return self.title


class Course(models.Model):
    owner = models.ForeignKey(User,
                              related_name='courses_created',
                              on_delete=models.CASCADE)
    subject = models.ForeignKey(Subject,
                                related_name='courses',
                                on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True)
    overview = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    students = models.ManyToManyField(User,
                                      related_name='courses_joined',
                                      blank=True)

    class Meta:
        ordering = ['-created']

    def __str__(self):
        return self.title


class Module(models.Model):
    course = models.ForeignKey(Course,
                               related_name='modules',
                               on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    description = models.TextField(blank=True)
    order = OrderField(blank=True, for_fields=['course'])

    class Meta:
            ordering = ['order']

    def __str__(self):
        return '{}. {}'.format(self.order, self.title)


class Content(models.Model):
    module = models.ForeignKey(Module,
                               related_name='contents',
                               on_delete=models.CASCADE)
    content_type = models.ForeignKey(ContentType,
                                     limit_choices_to={'model__in':('text',
                                                                    'video',
                                                                    'image',
                                                                    'file')},
                                     on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    item = GenericForeignKey('content_type', 'object_id')
    order = OrderField(blank=True, for_fields=['module'])

    class Meta:
            ordering = ['order']


class ItemBase(models.Model):
    owner = models.ForeignKey(User,
                              related_name='%(class)s_related',
                              on_delete=models.CASCADE)
    title = models.CharField(max_length=250)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True

    def __str__(self):
        return self.title

    def render(self):
        return render_to_string('courses/content/{}.html'.format(
            self._meta.model_name), {'item': self})


class Text(ItemBase):
    content = models.TextField()

class File(ItemBase):
    file = models.FileField(upload_to='files')

class Image(ItemBase):
       file = models.FileField(upload_to='images')

class Video(ItemBase):
    url = models.URLField()

Restaurant

from django.db import models
from django.utils.text import slugify
# Create your models here.

class Reservation(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField()
    phone = models.IntegerField()
    number_of_persons = models.IntegerField()
    Date = models.DateField()
    time = models.TimeField()


    def __str__(self):
        return self.name
        
        
class Meals(models.Model):
    name = models.CharField(max_length=50)
    description = models.TextField(max_length=500)
    category = models.ForeignKey('Category' , on_delete=models.SET_NULL , null=True)
    people = models.IntegerField()
    price = models.DecimalField(max_digits=5 , decimal_places=2)
    preperation_time =models.IntegerField()
    image = models.ImageField(upload_to='meals/')
    slug = models.SlugField(blank=True, null=True)


    def save(self , *args , **kwargs):
        if not self.slug and self.name :
            self.slug = slugify(self.name)
        super(Meals , self).save(*args , **kwargs)



    class Meta:
        verbose_name = 'meal'
        verbose_name_plural = 'meals'


    def __str__(self):
        return self.name


class Category(models.Model):
    name = models.CharField(max_length=30)


    class Meta:
        verbose_name = 'category'
        verbose_name_plural = 'categories'

    def __str__(self):
        return self.name

Signals

# signals.py
from core.models import Author, Tag, Post
from django.db.models.signals import post_save, m2m_changed
from django.dispatch import receiver
@receiver(post_save, sender=Author)
def author_changed(sender, instance, **kwargs):
    for post in instance.posts.with_documents():
        post.search_vector = post.document
        post.save(update_fields=['search_vector'])
@receiver(m2m_changed, sender=Post.tags.through)
def post_tags_changed(sender, instance, action, **kwargs):
    if action in ('post_add', 'post_remove', 'post_clear'):
        instance.save()
        
# apps.py
from django.apps import AppConfig


class CoreConfig(AppConfig):
    name = 'core'


    def ready(self):
        import core.signals  

Custom serializers for viewsets

# mixins.py
class ReadWriteSerializerMixin(object):
    """
    Overrides get_serializer_class to choose the read serializer
    for GET requests and the write serializer for POST requests.

    Set read_serializer_class and write_serializer_class attributes on a
    viewset. 
    """

    read_serializer_class = None
    write_serializer_class = None

    def get_serializer_class(self):        
        if self.action in ["create", "update", "partial_update", "destroy"]:
            return self.get_write_serializer_class()
        return self.get_read_serializer_class()

    def get_read_serializer_class(self):
        assert self.read_serializer_class is not None, (
            "'%s' should either include a `read_serializer_class` attribute,"
            "or override the `get_read_serializer_class()` method."
            % self.__class__.__name__
        )
        return self.read_serializer_class

    def get_write_serializer_class(self):
        assert self.write_serializer_class is not None, (
            "'%s' should either include a `write_serializer_class` attribute,"
            "or override the `get_write_serializer_class()` method."
            % self.__class__.__name__
        )
        return self.write_serializer_class
        
# views.py
from .mixins import ReadWriteSerializerMixin
from .models import MyModel
from .serializers import ModelReadSerializer, ModelWriteSerializer
class MyModelViewSet(ReadWriteSerializerMixin, viewsets.ModelViewSet):
    queryset = MyModel.objects.all() 
    read_serializer_class = ModelReadSerializer 
    write_serializer_class = ModelWriteSerializer

docker useful commands

$ docker-compose run -rm web ./manage.py [command] [arguments]
$ docker-compose run -rm web ./manage.py test [app]

celery

# pipenv install celery redis

# settings.py
CELERY_BROKER_URL = 'redis://redis:6379'
CELERY_RESULT_BACKEND = 'redis://redis:6379'
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'

# proj/celery.py
import os
from celery import Celery


os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'proj.settings')

app = Celery('proj')
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()

@app.task(bind=True)
def debug_task(self):
    print('Request: {0!r}'.format(self.request))

# app/tasks.py
from celery import shared_task


@shared_task
def hello():
    print('Hello there!')

@app.task
def test(arg=None):
    print(arg)

# proj/__init__.py
from .celery import app as celery_app
__all__ = ['celery_app']

# demo
from app.tasks import hello
hello.delay()

# crontab
# settings.py
from celery.schedules import crontab
CELERY_BEAT_SCHEDULE = {
    'hello': {
        'task': 'app.tasks.test',
        'schedule': crontab(hour=7, minute=30, day_of_week=1)  # Executes every Monday morning at 7:30 a.m,
        #'args': ('hello')
        
    }
}

# more info
# https://www.revsys.com/tidbits/celery-and-django-and-docker-oh-my/

celery dockerfile

#dockerfile.yml

services:
  db:
    image: postgres:9.6.5
    volumes:
      - postgres_data:/var/lib/postgresql/data/
  redis:
    image: "redis:alpine"
  web:
    build: .
    command: bash -c "python /code/manage.py migrate --noinput && python /code/manage.py runserver 0.0.0.0:8000"
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db
      - redis
  celery:
    build: .
    command: celery -A proj worker -l info
    volumes:
      - .:/code
    depends_on:
      - db
      - redis
  celery-beat:
    build: .
    command: celery -A proj beat -l info
    volumes:
      - .:/code
    depends_on:
      - db
      - redis

volumes:
  postgres_data:

Dockerize django

Dockerfile

# Dockerfile

# Pull base image
FROM python:3.8

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# Set work directory
WORKDIR /code

# Install dependencies
RUN pip install pipenv
COPY Pipfile Pipfile.lock /code/
RUN pipenv install --system

RUN apt-get update -y
RUN apt-get -y install apt-utils binutils libproj-dev gdal-bin


# Copy project
COPY . /code/

docker-compose.yml

version: '3.7'

services:
  
  db:
    image: kartoza/postgis:12.0
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    environment: 
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASS=${POSTGRES_PASS}
      - POSTGRES_MULTIPLE_EXTENSIONS=postgis,hstore,postgis_topology,pg_trgm,unaccent
    ports:
      - 5432:5432
    restart: always
  
    
  web:
    build: .
    command: bash -c "while !</dev/tcp/db/5432; do sleep 1; done; python manage.py makemigrations && python manage.py migrate && python manage.py runserver 0.0.0.0:8000 && pytest" 
    volumes:
      - .:/code
    ports:
      - 8000:8000
    depends_on:
      - db
     

volumes:
  postgres_data:

postgres settings

#....
DATABASES = {
    'default': {
        'ENGINE': 'django.contrib.gis.db.backends.postgis',
        'NAME': 'postgres',
        'USER': 'postgres',
        'PASSWORD':'postgres',
        'HOST': 'db', # set in docker-compose.yml
        'PORT': 5432, # default postgres port
        'CONN_MAX_AGE': 1000,  # pooling

    }
}

run commands

$ docker-compose run --rm web python /code/manage.py createsuperuser

Upload

# settings.py
MEDIA_URL = '/media/'
#MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_ROOT = '/media'

STATIC_URL = "/static/"
STATIC_ROOT="/static/"

# urls.py -> demo
from django.conf import settings
from django.conf.urls.static import static # new
if settings.DEBUG: # new
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    
# models.py
class File(models.Model):
    image = models.ImageField(upload_to='images/')

# serializers.py
class FileSerializer(serializers.ModelSerializer):
    class Meta:
        fields = (
            'id',
            'image'
        )
        model = models.File
 
 # views.py
 class FileViewSet(viewsets.ModelViewSet):
    queryset = models.File.objects.all()
    serializer_class = serializers.FileSerializer
    
    
# urls.py
# ...
 router.register('files', views.FileViewSet)

Dockerfile

# Dockerfile

# Pull base image
FROM python:3.8

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ENV POSTGRES_USER postgres
ENV POSTGRES_PASS postgres
ENV BIZAO_CLIENT_ID ugyrNwqHv6bX9CB1K8tjmqtvDIYa
ENV BIZAO_CLIENT_SECRET OSQ47FxbhsWrWNfz3FP2ITWLmVsa
ENV BIZAO_TOKEN dWd5ck53cUh2NmJYOUNCMUs4dGptcXR2RElZYTpPU1E0N0Z4YmhzV3JXTmZ6M0ZQMklUV0xtVnNh

# Set work directory
WORKDIR /code


# Install dependencies
RUN pip install pipenv
COPY Pipfile Pipfile.lock /code/
RUN pipenv install --system

RUN apt-get update -y
RUN apt-get -y install apt-utils binutils libproj-dev gdal-bin


# Copy project
COPY . /code/

docker-compose.yml

version: '3.7'

services:
  
  db:
    image: kartoza/postgis:12.0
    volumes:
      - adafri_postgres:/var/lib/postgresql/data/
    environment: 
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASS=${POSTGRES_PASS}
    ports:
      - 5432:5432
    restart: always
    
    
  web:
    build: .
    command: bash -c "while !</dev/tcp/db/5432; do sleep 1; done; python manage.py collectstatic --noinput && python manage.py makemigrations && python manage.py migrate  && gunicorn api.wsgi:application --bind 0.0.0.0:8000" 
    volumes:
      - .:/code
      - static:/static   
      - media:/media   
    expose: 
      - 8000
    depends_on:
      - db
    

  nginx:
    image: nginx:latest
    ports:
      - 8000:80
    volumes:
      - ./nginx:/etc/nginx/conf.d
      - static:/static  
      - media:/media 
    depends_on:
      - web
   

volumes:
  adafri_postgres:
  static:
  media:

nginx

# nginx/nginx.conf

 
upstream web {
    server web:8000;
}

server {

    listen 80;
    server_name localhost;

    location /static/ {
        alias /static/;
    }

    location /media/ {
        alias /media/;
    }

    location / {
        proxy_pass http://web/;
        proxy_set_header Host $http_host;
        #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        #proxy_set_header X-Forwarded-Protocol "";
        #proxy_set_header X-Forwarded-Ssl "";
    }


    
}

Queries

Query for each item in a list

def get_queryset(self):
    queryset = super(MyModel, self).get_queryset()
    states_filter = [
        item.strip()
        for item in config.STATES.split(',')
    ]
    if not states_filter:
        MyModel.objects.none()
    q = Q()
    for state in states_filter:
        q = q | Q(status__iexact=state)
    queryset = queryset.filter(q)
    return queryset

Dynamic fields in Django Rest Framwork serializers

from django.db import models
from django.utils import timezone


class Author(models.Model):
    name = models.CharField(max_length=40)
    dob = models.DateField(verbose_name='Date of birth')

    @property
    def age(self):
        return timezone.now().year - self.dob.year

class AuthorSerializer(serializers.ModelSerializer):
    age = serializers.SerializerMethodField()

    class Meta:
        model = Author
        fields = '__all__'

    def get_age(self, instance):
        return datetime.datetime.now().year - instance.dob.year


class AuthorSerializer(serializers.ModelSerializer):
    age = serializers.SerializerMethodField(method_name='calculate_age')

    class Meta:
        model = Author
        fields = ('id', 'name', 'dob', 'age')

    def calculate_age(self, instance):
        request = self.context.get('request')
        user = request.user
        if user.is_authenticated() and user.is_staff:
            return datetime.datetime.now().year - instance.dob.year
        return 'Hidden'

get choice display name

class Team(models.Model):
    LEADERSHIP_TEAM = 1
    ACCOUNT_TEAM = 2
    CREATIVE_TEAM = 3
    TEAM_CHOICES = (
        (LEADERSHIP_TEAM, 'Leadership'),
        (ACCOUNT_TEAM, 'Account'),
        (CREATIVE_TEAM, 'Creative'),
    )

    team = models.IntegerField(choices=TEAM_CHOICES, default=LEADERSHIP_TEAM)

    ...
{% for member in object_list %}
    {{ member.get_team_display|lower }}
{% endfor %}
get_user_type_display()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment