Django is a popular web framework that allows developers to build powerful and scalable web applications. One of the essential components of any web application is the database, and Django makes it easy to create models that map to a database’s tables.
In this article, we’ll explore how to create Django models, define relationships with other models, and set primary keys and unique constraints.
Creating Django Models
Django models are Python classes that define the structure of a database table. Here’s an example of a simple Django model:
from django.db import models
class Pet(models.Model):
name = models.CharField(max_length=50)
breed = models.CharField(max_length=50)
owner = models.ForeignKey('PetOwner', on_delete=models.CASCADE)
In this model, we define a Pet class that inherits from Django’s Model class.
The Pet class has three fields: name, breed, and owner. The name and breed fields are CharFields, which means they store strings of up to 50 characters.
The owner field is a ForeignKey that links to another PetOwner model.
Defining Relationships with Other Models
Django models can have relationships with other models, which allows us to build complex data structures. In the Pet model example above, we defined a ForeignKey relationship between the Pet and PetOwner models.
This relationship means that each Pet has one Owner, and each Owner can have many Pets. To define a relationship in Django, we use the ForeignKey field.
The ForeignKey field takes two arguments: the model class it’s related to and the on_delete argument. The related model argument specifies which model the relationship is with, while the on_delete argument specifies what should happen when the related model is deleted.
Error When Skipping on_delete Argument
One common error that developers encounter is when they skip the on_delete argument. For example, if we modified the Pet model to remove the on_delete argument like this:
class Pet(models.Model):
name = models.CharField(max_length=50)
breed = models.CharField(max_length=50)
owner = models.ForeignKey('PetOwner')
Django will raise a warning, telling us to explicitly set the on_delete argument.
If we ignore the warning, we’ll get an error when we try to run migrations:
TypeError: __init__() missing 1 required positional argument: 'on_delete'
Reproducing the Error
We’ll now create two models, PetOwners, and Pet, and create a relationship between them. Here’s what the models look like:
class PetOwner(models.Model):
name = models.CharField(max_length=50)
class Pet(models.Model):
name = models.CharField(max_length=50)
breed = models.CharField(max_length=50)
owner = models.ForeignKey('PetOwner')
Now, run the command python manage.py makemigrations
to create the migrations.
Django will give you a warning indicating that we should set on_delete.
Fixing the Error
To fix the error, we need to explicitly set the on_delete argument. The on_delete argument tells Django what to do when the related model is deleted.
There are several possible values for the on_delete argument, including CASCADE, PROTECT, SET_NULL, SET_DEFAULT, SET(), and DO_NOTHING. In our example, we want to delete all Pets when the PetOwner is deleted.
So, we’ll set the on_delete argument to CASCADE:
class Pet(models.Model):
name = models.CharField(max_length=50)
breed = models.CharField(max_length=50)
owner = models.ForeignKey('PetOwner', on_delete=models.CASCADE)
Now, if we run the migrations again, no warning will come.
Primary Keys and Unique Constraints in Django
Two essential concepts in databases are primary keys and unique constraints. A primary key is a field or combination of fields that uniquely identify each row in a table.
A unique constraint is a way to ensure that no two rows in a table have the same values for specific fields.
Setting Primary Keys
In Django, the default primary key is an auto-incrementing integer field called id. However, we can also define our own primary keys using the primary_key argument.
For example, suppose we want to use a Pet’s name as the primary key. In that case, we can modify our Pet model like this:
class Pet(models.Model):
name = models.CharField(max_length=50, primary_key=True)
breed = models.CharField(max_length=50)
owner = models.ForeignKey('PetOwner', on_delete=models.CASCADE)
Defining Unique Constraints
To define unique constraints in Django, we use the unique argument. For example, suppose we want to ensure that no two Pets have the same name and breed.
In that case, we can modify our Pet model like this:
class Pet(models.Model):
name = models.CharField(max_length=50)
breed = models.CharField(max_length=50)
owner = models.ForeignKey('PetOwner', on_delete=models.CASCADE)
class Meta:
unique_together = ('name', 'breed')
Upserting with Unique Constraints
Upserting is the process of updating a row in a table if it exists, or inserting a new row if it doesn’t. In Django, we can use the update_or_create method to achieve this.
To use update_or_create with unique constraints, we need to specify the unique fields using the defaults argument. Here’s an example:
Pet.objects.update_or_create(
name='Fido', breed='German Shepherd',
defaults={'owner': PetOwner.objects.get(name='John')}
)
In this case, if a Pet with the name ‘Fido’ and breed ‘German Shepherd’ doesn’t exist, Django will create a new Pet with those values and assign it to the PetOwner named John.
However, if a Pet with those values already exists, Django will update its owner field with John.
Conclusion
In this article, we’ve covered several essential concepts related to Django models and database relationships. We’ve explored how to create Django models, define relationships with other models, set primary keys and unique constraints, and upsert data using unique constraints.
By understanding these concepts, you’ll be able to create powerful and scalable web applications with Django.
Querying Data in Django
Once a model is defined in Django, we can retrieve and manipulate the data stored in its underlying table. We can easily retrieve objects (or rows) from the table using queries.
Django Querysets allow us to retrieve, filter, sort, and delete data from the database in Python code.
Retrieving Objects from Models
To retrieve objects from the models, we can use Django’s Model.objects.all() method. This method returns a Queryset containing all the objects in the model’s table.
If we want to retrieve a specific object from a model’s table, we can use the Model.objects.get() method. This will return a single object that matches the specified query.
class Person(models.Model):
name = models.CharField(max_length=50)
age = models.IntegerField()
Person.objects.all() # returns all Person objects
Person.objects.get(name='John') # returns the Person object with the name 'John'
Filtering Querysets
Django provides several methods to filter Querysets based on certain conditions. The most commonly used method is filter().
This method allows us to retrieve only the objects that match a specified condition. “`
class Person(models.Model):
name = models.CharField(max_length=50)
age = models.IntegerField()
Person.objects.filter(age__gte=18) # returns all Person objects whose age is greater than or equal to 18
In the above example, we use the filter() method to list all objects whose age is greater than or equal to 18.
A similar way to filter objects is to use the exclude() method to exclude objects that match a condition. “`
class Person(models.Model):
name = models.CharField(max_length=50)
age = models.IntegerField()
Person.objects.exclude(age__lt=18) # returns all Person objects except those whose age is less than 18.
Querying Related Objects
Django also provides an easy way to query related objects. We can use the double-underscore( __ ) notation to follow a relationship between models and retrieve related objects.
class Pet(models.Model):
name = models.CharField(max_length=50)
class Person(models.Model):
name = models.CharField(max_length=50)
pet = models.ForeignKey(Pet, on_delete=models.CASCADE)
def __str__(self):
return f'{self.name} - {self.pet.name}'
Person.objects.filter(pet__name="Fido") # returns all Person objects whose pet's name is Fido.
In the above example, we query the Person model’s related Pet model by using the double-underscore notation.
We have used pet__name
to filter using the pet’s name. Django’s Class-Based Views
The Class-Based Views(CBVs) in Django provides a structured and reusable way to build views for web applications.
Compared to function-based views, CBVs are more readable, maintainable, and modular.
Understanding CBVs
CBVs are Django’s implementation of object-oriented views. They are classes that define how to render an HTTP response given an HTTP request.
CBVs provide a clear separation of concerns between the HTTP request and response handling. This separation of concerns encourages modular and reusable code.
Coding a ListView
The ListView is a CBV that retrieves a list of objects from a model and passes it to a template. The ListView is used to render a list of objects from a model that match a particular condition specified as the URL parameter.
from django.views.generic.list import ListView
from .models import Product
class ProductListView(ListView):
model = Product
template_name = "product_list.html"
In the above example, we define a ProductListView that retrieves all objects from the Product model and renders them using the product_list.html
template.
Implementing DetailView
The DetailView is a CBV that displays detailed information about a specific object. It retrieves a single object from a model and passes it to a template for rendering.
from django.views.generic.detail import DetailView
from .models import Product
class ProductDetailView(DetailView):
model = Product
template_name = "product_detail.html"
In the above example, we define a ProductDetailView that retrieves a single product object from the Product model and renders it using the product_detail.html
template.
Creating a FormView
The FormView is a CBV that is used to handle forms. It provides a consistent way to handle form submission and display a form to edit an object.
from django.views.generic.edit import FormView
from django.urls import reverse_lazy
from .forms import ContactForm
class ContactFormView(FormView):
form_class = ContactForm
template_name = 'contact.html'
success_url = reverse_lazy('contact_success')
def form_valid(self, form):
form.save()
return super().form_valid(form)
In the above example, we define a ContactFormView that uses ContactForm to display a form in the contact.html
template. When the form is submitted successfully, it redirects to the URL represented by the ‘contact_success’ URL pattern.
Conclusion
Django’s Querysets and Class-Based Views are powerful and flexible tools that allow developers to interact with data and build robust web applications. Understanding how to leverage these tools within the framework can help you save time and build more flexible, maintainable software.
With these concepts, developers can create web applications that interact with databases in a modular and reusable way.
Django Authentication
Django Authentication is the process of verifying the identity of a user. It allows users to login to an application and access its features.
Django provides a robust and secure authentication system out of the box that can be customized and extended to fit specific requirements.
Understanding Authentication
Django Authentication is built around a User model. The User model provides authentication-related functionalities such as password hashing, checking, and resetting.
The Authentication system in Django uses sessions to keep track of user login status. When a user logs in, a session is created, and a session key is stored in the user’s browser as a cookie.
This session key is then used to identify subsequent requests from the user.
Customizing User Model
Django’s default User model might not work for some specific applications. However, Django provides the flexibility to override or customize the default User model.
We can extend the User model to store additional data by creating a new model that inherits from the User model. “`
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
age = models.PositiveIntegerField(null=True, blank=True)
In the above example, we create a CustomUser model that inherits from the AbstractUser model.
We add an age field to the CustomUser model as an example. Once the CustomUser model is created, we have to tell Django to use it instead of the default User model for authentication.
We can do this by updating the AUTH_USER_MODEL setting in the project’s settings.py file:
AUTH_USER_MODEL = 'app.CustomUser'
Adding Login Functionality
Django provides APIs to handle user authentication. To add login functionality, we can use the built-in authentication views and forms provided by Django.
from django.contrib.auth.views import LoginView
class CustomLoginView(LoginView):
template_name = "app/login.html"
success_url = reverse_lazy("app:home")
In the above example, we create a CustomLoginView that inherits from Django’s built-in LoginView class. We specify the template and the URL to redirect to upon successful login.
Once the view is created, we need to define the URL to handle the login request:
from django.urls import path
from .views import CustomLoginView
app_name = "app"
urlpatterns = [
path("login/", CustomLoginView.as_view(), name="login"),
]
In the above example, we define a URL pattern for the login view.
Implementing Logout Functionality
In Django, to implement logout functionality, we can use the built-in logout view provided by the authentication framework. “`
from django.contrib.auth.views import LogoutView
class CustomLogoutView(LogoutView):
template_name = "app/logged_out.html"
In the above example, we create a CustomLogoutView that inherits from Django’s built-in LogoutView class.
We specify the template to be displayed after the user logs out. Once the view is created, we need to define the URL pattern for the logout view:
from django.urls import path
from .views import CustomLogoutView
app_name = "app"
urlpatterns = [
path("logout/", CustomLogoutView.as_view(), name="logout"),
]
In the above example, we define a URL pattern for the logout view.
Conclusion
By customizing the User model, adding login functionality, and implementing logout functionality, we can build authentication features for our Django application that are secure and user-friendly. The default Django authentication system provides a robust and secure framework, but it allows for customization and extension, which makes it flexible enough to meet a wide range of authentication needs.
By understanding and making use of Django authentication, developers can build secure, scalable, and user-friendly web applications with ease. In conclusion,
Django Authentication is a crucial aspect of web application development that allows users to access an application’s features securely.
Understanding the authentication system, customizing the User model, adding login functionality, and implementing logout functionality are essential aspects of building a secure and user-friendly web application. By leveraging Django’s built-in authentication system, developers can create powerful and scalable applications that meet the specific requirements of an organization.
Therefore, it is crucial to learn and implement Django authentication in web application development.