Skip to content

Instantly share code, notes, and snippets.

@fxadecimal
Last active November 22, 2021 15:23
Show Gist options
  • Save fxadecimal/f4cc6228a204df200a135073698c27f4 to your computer and use it in GitHub Desktop.
Save fxadecimal/f4cc6228a204df200a135073698c27f4 to your computer and use it in GitHub Desktop.
Django 3+ Snippets

Django 3+ snippets

Virtual Env

pip3 install virtualenvwrapper

mkvirtualenv $ENV && \
workon $ENV

pip install -r requirements.txt

or

# on ubuntu:
sudo apt-get install python3-pip

pip3 install virtualenv
virtualenv .venv && source .venv/bin/activate
#!/usr/bin/env bash

export VIRTUALENVWRAPPER_PYTHON=`which python3`
source `which virtualenvwrapper.sh`

workon $ENV

cd server
./manage.py runserver 0.0.0.0:8000

Managment Commands

# Generate a random secret_key
openssl rand -base64 42

# Migrate some stuff
./manage.py makemigrations && ./manage.py migrate

# Create a new developer SU account
./manage.py createsuperuser --username me@domain.com --email me@domain.com

# Dump Django data out to JSON file, excluding contenttype, admin & auth
./manage.py dumpdata --exclude auth.permission --exclude contenttypes --exclude admin --exclude sessions --indent 2

# Dump Django Apps: accounts & profiles
./manage.py dumpdata accounts profiles --indent 2

# Output Graphviz database diagram (With Django Extensions installed)
./manage.py graph_models app > app.dot

Useful Shell Commands

# delete pycache dirs
find . -type d -name __pycache__ -delete

# delete migtations
find . -path "*/migrations/*.py" -not -name "__init__.py" -delete

Models

TimestampedModel

# Inspired by, https://github.com/gothinkster/django-realworld-example-app/blob/master/conduit/apps/core/models.py

from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
app_label = 'common'

class TimestampedModel( models.Model ):
    created_at = models.DateTimeField(auto_now_add=True,null=True)
    updated_at = models.DateTimeField(auto_now=True, null=True)

    class Meta:
        abstract = True
        ordering = ['-created_at', '-updated_at']

Custom User in 2021

Inspired by https://github.com/wsvincent/drfx/blob/master/users/models.py

from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    name = models.CharField(blank=True, max_length=255)
    phone_number = models.CharField(blank=True, max_length=255)

    def __str__(self):
        return self.email

Followable UserProfile

Inspired by https://github.com/gothinkster/django-realworld-example-app/blob/master/conduit/apps/profiles/models.py

from django.db import models
from django.contrib.auth import get_user_model
from django.dispatch import receiver
from django.db.models.signals import post_save

User = get_user_model()

class UserProfile( TimestampedModel ):
    user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE, editable=False)
    
    follows = models.ManyToManyField(
        'self',
        related_name='followed_by',
        symmetrical=False,
        through='Follows',
        blank=True,
    )
    
    def follow(self, profile):
        """Follow `profile` if we're not already following `profile`."""
        self.follows.add(profile)

    def unfollow(self, profile):
        """Unfollow `profile` if we're already following `profile`."""
        self.follows.remove(profile)

    def is_following(self, profile):
        """Returns True if we're following `profile`; False otherwise."""
        return self.follows.filter(pk=profile.pk).exists()

    def is_followed_by(self, profile):
        """Returns True if `profile` is following us; False otherwise."""
        return self.followed_by.filter(pk=profile.pk).exists()
        
class Follows( models.Model ):
    profile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name="+")
    follows = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name="+")

    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        profile, follows = self.profile, self.follows
        return f'Follows( {profile}, {follows} )'

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        profile = UserProfile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.profile.save()

Many to many through

class UserGroup(models.Model):
    members = models.ManyToManyField(User, through='UserGroupMembership')

class UserGroupMembership(models.Model):
    person = models.ForeignKey(User, on_delete=models.CASCADE)
    group = models.ForeignKey(UserGroup, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)

Basic Class Based Views

from django.shortcuts import render
from django.views.generic import CreateView, DetailView, FormView, UpdateView
from django.views.generic.list import ListView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse, reverse_lazy
from django.contrib.auth import get_user_model

User = get_user_model()

from .models import Item

class Create( CreateView ):
    model = Item
    fields = '__all__'
    success_url = reverse_lazy('items')
    
    
class Update( UpdateView ):
    pass
    
class Detail( DetailView ):
    pass
    
class Delete( DeleteView ):
    pass

class List( ListView ):
    pass

Simple ImageField resized

django-resized

"""
<div style="width:250px; height:250px;background-position: center;  background-image: url({{user.avatar.url}}); background-size: cover;" class="border border-primary">
"""

from django_resized import ResizedImageField
from uuid import uuid4
import pathlib, os
from django.conf import settings

def custom_location(instance, filename):
    user_id  = str(instance.uuid)
    ext = pathlib.Path( filename ).suffix
    filename = f'{user_id}{ext}'
    return os.path.join('media','avatars',filename )
    

class CustomUser(AbstractUser):
    uuid = models.UUIDField(default=uuid4)
    avatar = ResizedImageField(blank=True, 
        null=True, 
        crop=['middle', 'center'], 
        keep_meta=False, 
        upload_to=custom_location, 
        force_format='JPEG')
        

Disable AllAuth Account Registration

reference

#models.py
from allauth.account.adapter import DefaultAccountAdapter

class MyAccountAdapter(DefaultAccountAdapter):    
    def is_open_for_signup(self, request):
        return False
        
#settings.py
ACCOUNT_ADAPTER = 'models.MyAccountAdapter'

Signals

from django.db.models.signals import pre_delete 
from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(pre_delete, sender=Dataset)
def delete_related_journal(sender, instance, **kwargs):
    dataset = instance
    instance.some_pre_delete_method
    
@receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
    user = instance
    if created:
        profile = UserProfile(user=user, creator=user, first_name=user.first_name, last_name=user.last_name)

CSFR_exemption for CBV

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator

@method_decorator(csrf_exempt, name='dispatch')
class JsonDataset(DetailView):
    def post(self, *args, **kwargs):
        """
        curl -d "param1=value1&param2=value2" -X POST http://localhost:8000/api/v1/test
        curl -d '{"key1":"value1", "key2":"value2"}' -H "Content-Type: application/json" -X POST http://localhost:8000/api/v1/test
        """
        
        if self.request.headers['Content-Type'].strip() == 'application/json':
            params = json.loads( self.request.body )
        else:
            params = self.request.POST.dict()
            
        return JsonResponse(params, json_dumps_params={'indent': 1} )

Handy Snippets

class UpdateProfile(UpdateView, LoginRequiredMixin):
    model = models.UserProfile
    template_name = 'cbv/form.html'
    fields = '__all__'

    def get_object(self, *args, **kwargs):
        profile, _ = self.model.objects.get_or_create(user=self.request.user)
        return profile
# Render everthing at /pages from pages directory

from django.shortcuts import render
from django.contrib.auth.decorators import login_required

@login_required
def get_html(request, resource):
    return render(request, f'pages/{resource}')

# urls.py
urlpatterns = [
    path('pages/<path:resource>', views.get_html , name='pages'),
]
# Ingest Posted data
# curl -F 'files=@./requirements.txt' http://localhost:8000/api/ingest

from django.http.response import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def ingest_stuff(request):
    if request.method == 'GET':
        # return render(request, 'upload.html')
        return HttpResponse("""<form method="POST" enctype="multipart/form-data"> <input type="file" name="files" multiple> <input type="submit" value="Upload" /></form>""")
    
    # ingest: json, form, data, file
    headers = dict( request.headers )
    print(request.FILES )
    return JsonResponse(dict(headers=headers), safe=False)
 

References

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