Notes taken while working on Social project

Using and modifying User authentication models and forms

In models.py

from django.contrib import auth

    # Create your models here.
    class User(auth.models.User,auth.models.PermissionsMixin):

        def __str__(self):
            return "@{}".format(self.username)

In forms.py

from django.contrib.auth import get_user_model
    from django.contrib.auth.forms import UserCreationForm

    class UserCreateForm(UserCreationForm):
        class Meta:
            model = get_user_model()
            fields = ('username','email','password1','password2')

        #Changing the labels for the fields
        def __init__(self,*args,**kwargs):
            super().__init__(*args,**kwargs)
            self.fields['username'].label = 'Display Name'
            self.fields['email'].label = 'Email Address'

In views.py

# Create your views here.
    #accounts/signup/
    class SignUp(CreateView):
        form_class = forms.UserCreateForm
        success_url = reverse_lazy('login')
        template_name = 'accounts/signup.html'

Using bootstarp themes with django

Install

pip install django-bootstrap4

In settings.py

INSTALLED_APPS = [
    ...
    'bootstrap4',
    ...
]

Using them in templates : accounts/signup.html

{%  extends "base.html"  %}

<!-- using django-bootstrap -->
{%  load bootstrap4  %}

{%  block content  %}
<div class="jumbotron">
  <h1>Sign up</h1>
</div>

<div class="container">
  <form method="post">
    {%  csrf_token  %}
    <!-- loading bootstrap styles -->
    {%  bootstrap_form form  %}
    <input type="submit" class="btn btn-primary" value="Sign Up">
  </form>
</div>

{%  endblock  %}

Login and LogoutView from auth

In urls.py

from django.contrib.auth import views as auth_views

    path('login/',auth_views.LoginView.as_view(template_name='accounts/login.html'),name='login'),
    path('logout/',auth_views.LogoutView.as_view(),name='logout'),

Set up redirect urls in settings.py

LOGIN_REDIRECT_URL = '/loginsuccess'
LOGOUT_REDIRECT_URL = '/thanks'

More about Models

Very useful documentation https://docs.djangoproject.com/en/2.1/ref/models/

Sub heading in this documentation

Model field reference (list of model Fields and their options)
Field attribute reference
Model index reference
Model _meta API
Related objects reference
Model class reference
Model Meta options (Model metadata is “anything that’s not a field”)
Model instance reference
QuerySet API reference
Lookup API reference
Query Expressions
Conditional Expressions
Database Functions

Model Meta options

Model metadata is “anything that’s not a field”, such as ordering options (ordering), database table name (db_table),

usage example

class GroupMember(models.Model):
    group = models.ForeignKey(Group,on_delete=models.CASCADE,related_name='memberships')
    user = models.ForeignKey(User,on_delete=models.CASCADE,related_name='user_group')

    def __str__(self):
        return self.user.username

    #For list of model Meta options:
    # ref: https://docs.djangoproject.com/en/dev/ref/models/options/
    class Meta:
        #Sets of field names that, taken together, must be unique:
        unique_together = (group,user)

SlugField

A slug is a for something, containing only letters, numbers, underscores or hyphens (ie chars accepted in url).

#allow_unicode : If True, the field accepts Unicode letters in addition to ASCII letters. Defaults to False.
    #ref: https://docs.djangoproject.com/en/2.1/ref/models/fields/
    slug = models.SlugField(allow_unicode=True.unique=True)

This is useful while sending strings as parameters to url

usage example

#Helps in passing strings as parameters to url
from django.utils.text import slugify
#helps in using markdown inside of the post
#pip install misaka
import misaka

# Create your models here.
class Group(models.Model):
    name = models.CharField(max_length=255,unique=True)

    # A slug is a for something, containing only letters, numbers, underscores or hyphens (ie chars user in url).
    #allow_unicode : If True, the field accepts Unicode letters in addition to ASCII letters. Defaults to False.
    #ref: https://docs.djangoproject.com/en/2.1/ref/models/fields/
    slug = models.SlugField(allow_unicode=True.unique=True)

    description = models.TextField(blank=True,default='')
    #Used in case we want html version of our description
    #editable (option available for all fields):
    #If False, the field will not be displayed in the admin or any other ModelForm. They are also skipped during model validation. Default is True.
    description_html =  models.TextField(editable=False,default='',blank=True)

    # Django will automatically generate a table to manage many-to-many relationships.
    # However, if you want to manually specify the intermediary table,
    # you can use the through option to specify the Django model that represents the intermediate table that you want to use.
    members = models.ManyToManyField(User,through='GroupMember')

    def __str__(self):
        return self.name

    #Funtion in models.Model that saves model to db
    def save(self,*args,**kwargs):
        #we set slug as the name(unique) of the group
        self.slug = slugify(self.name)
        #processes markdown
        self.description_html = misaka.html(self.description)
        super().save(*args,**kwargs)

    def get_absolute_url(self):
        #app_name = group
        #path("posts/in/<slug>",views.SingleGroup.as_view(),name="single")
        return reverse('groups:single',kwargs={'slug':self.slug})

Getting model of current User session

When User is implemented in another application of same project. we can get that object as follows,

from django.contrib.auth import get_user_model
#get model of the current user session
User = get_user_model()
#This User is used as Foreign key in GroupMembers class. User is implemented in another application of same project

Getting model objects

By default, Django adds a Manager with the name objects to every Django model class.

A Manager is the interface through which database query operations are provided to Django models. At least one Manager exists for every model in a Django application.

The way Manager classes work is documented in Making queries

Getting all objects

all_obj_list = Post.objects.all()

Getting specif object

self.post_user = User.objects.get(username__iexact=self.request.user.username)

Filtering

#members is a field in Group model
context["groups_of_user"] = Group.objects.filter(members__in=[self.request.user])

Another way to get

membership = GroupMember.objects.filter(user=self.request.user,group__slug=self.kwargs.get('slug')).get()

Using get_queryset

The following code grabs all Post object from database

class PostList(generic.ListView):
    model = models.Post

But sometimes we want just the objects that match certain criteria. For example, to get Post object having a logged in username

def get_queryset(self):
    queryset = super().get_queryset()
    #post.user.username
    return queryset.filter(user__username__iexact=self.request.user.username)

NOTE: Do not use request.user without login check

To get Post object of any specific user

from django.http import Http404

#Under class UserPost
def get_queryset(self):
    try:
        self.post_user = User.objects.get(username__iexact=self.kwargs.get("username"))
    except User.DoesNotExist:
        #executed when there is exception
        raise Http404
    else:
        #this statement will be executed when there is no exception
        return self.post_user.posts.all()

self.kwargs is available for url

##posts/by/<username>
path('by/<username>',views.UserPost.as_view(),name='user_post')

More about views

Using reverse instead of reverse_lazy breaks compilation with circular import error

success url overrides get_absolute_url defined in Models

class CreateGroup(LoginRequiredMixin,generic.CreateView):
    fields = ('name','description')
    model = Group
    template_name = "groups/group_form.html"
    #Using reverse instead of reverse_lazy breaks compilation with circular import error
    #success url overrides get_absolute_url defined in Models
    success_url = reverse_lazy("index")

Using Redirect View

You dont specify a model in this class. You just redefine get and redirect methods

class JoinGroup(LoginRequiredMixin,generic.RedirectView):
    #We have to get a specific Group object and create a new GroupMember

    def get(self,request,*args,**kwargs):
        group = get_object_or_404(Group,slug=self.kwargs.get('slug'))

        try:
            GroupMember.objects.create(user=self.request.user,group=group)
        except IntegrityError:
            messages.warning(self.request,'Warning! Already a member')
        else:
            messages.success(self.request,'You are now a member!')

        return super().get(request,*args,**kwargs)

    def get_redirect_url(self,*args,**kwargs):
        return reverse("groups:single",kwargs={'slug':self.kwargs.get('slug')})

class LeaveGroup(LoginRequiredMixin,generic.RedirectView):

    def get(self,request,*args,**kwargs):
        try:
            membership = GroupMember.objects.filter(user=self.request.user,group__slug=self.kwargs.get('slug')).get()
        except GroupMember.DoesNotExist:
            messages.warning(self.request,'Sorry You are not in this group')
        else:
            membership.delete()
            messages.success(self.request,'You are not a member anymore!')

        return super().get(request,*args,**kwargs)

    def get_redirect_url(self,*args,**kwargs):
        return reverse("groups:single",kwargs={'slug':self.kwargs.get('slug')})

Injecting html (include tag)

Often times, when html becomes too long, we may want to break it up in separate files and inject them. This is done with include tag

{%  include "posts/_post.html"  %}

_posts is just a convention for developers to understand that _post.html is a part of another html file