Django Polls Simplified — Playing with the Database API
This is a continuation:
Now, let’s hop into the interactive Python shell and play around with the free API Django gives you. To invoke the Python shell, use this command:
python manage.py shellOnce you’re in the shell, explore the database API:
Import the models:
from polls.models import Choice, QuestionGet all questions:
Question.objects.all()Create A Question:
# Create a new Question.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
from django.utils import timezone
q = Question(question_text="What's the best MacBook Pro?", pub_date=timezone.now())
# Save the object into the database. You have to call save() explicitly.
q.save()
# Now it has an ID
q.id
# Access model field values via Python attributes.
q.question_text
# "What's the best MacBook Pro?"
q.pub_date
# datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=datetime.timezone.utc)
# Change values by changing the attributes, then calling save().
q.question_text = "What's the best MacBook?"
q.save()
# objects.all() displays all the questions in the database.
Question.objects.all()
# <QuerySet [<Question: Question object (1)>]>Wait a minute. <Question: Question object (1)> isn’t a helpful representation of this object. Let’s fix that by editing the Question model (in the polls/models.py file) and adding a __str__() method to both Question and Choice:
from django.db import models
class Question(models.Model):
# ...
def __str__(self):
return self.question_text
class Choice(models.Model):
# ...
def __str__(self):
return self.choice_textUpdated models.py:
from django.db import models
class Question(models.Model):
"""
A model representing a poll question.
Attributes:
question_text (CharField): The text of the question.
pub_date (DateTimeField): The date and time when the question was published.
"""
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField("date published")
def __str__(self):
return self.question_text
class Choice(models.Model):
"""
Represents a choice for a question in a poll.
Attributes:
question (ForeignKey): The question this choice is associated with.
choice_text (CharField): The text of the choice.
votes (IntegerField): The number of votes this choice has received.
"""
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_textIt’s important to add __str__() methods to your models, not only for your convenience when dealing with the interactive prompt but also because objects’ representations are used throughout Django’s automatically generated admin.
Let’s also add a custom method to this model:
import datetime
from django.db import models
from django.utils import timezone
class Question(models.Model):
# ...
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)Save these changes and start a new Python interactive shell by running python manage.py shell again:
from polls.models import Choice, Question
# Make sure our __str__() addition worked.
Question.objects.all()
# <QuerySet [<Question: What's the best MacBook?>]>
# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
Question.objects.filter(id=1)
# <QuerySet [<Question: What's the best MacBook?>]>
Question.objects.filter(question_text__startswith="What")
# <QuerySet [<Question: What's the best MacBook?>]>
# Get the question that was published this year.
from django.utils import timezone
current_year = timezone.now().year
Question.objects.get(pub_date__year=current_year)
# <Question: What's the best MacBook?>
# Request an ID that doesn't exist, this will raise an exception.
Question.objects.get(id=2)
## Response Below
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/ca/miniconda3/envs/django-polls/lib/python3.12/site-packages/django/db/models/manager.py", line 87, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/ca/miniconda3/envs/django-polls/lib/python3.12/site-packages/django/db/models/query.py", line 649, in get
raise self.model.DoesNotExist(
polls.models.Question.DoesNotExist: Question matching query does not exist.
# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Question.objects.get(id=1).
Question.objects.get(pk=1)
# <Question: What's the best MacBook?>
# Make sure our custom method worked.
q = Question.objects.get(pk=1)
q.was_published_recently()
# True
# Give the Question a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set (defined as "choice_set") to hold the "other side" of a ForeignKey
# relation (e.g. a question's choice) which can be accessed via the API.
q = Question.objects.get(pk=1)
# Display any choices from the related object set -- none so far.
q.choice_set.all()
# <QuerySet []>
# Create three choices.
q.choice_set.create(choice_text="MacBook Pro M1, 2020", votes=0)
q.choice_set.create(choice_text="MacBook Air M1, 2021", votes=0)
c = q.choice_set.create(choice_text="MacBook Pro M4", votes=0)
# Choice objects have API access to their related Question objects.
c.question
# <Question: What's the best MacBook?>
# And vice versa: Question objects get access to Choice objects.
q.choice_set.all()
# <QuerySet [<Choice: MacBook Pro M1, 2020>, <Choice: MacBook Air M1, 2021>, <Choice: MacBook Pro M4>]>q.choice_set.count()
# 3
# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
Choice.objects.filter(question__pub_date__year=current_year)
# <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# Let's delete one of the choices. Use delete() for that.
c = q.choice_set.filter(choice_text__startswith="JMacBook Pro M4")
c.delete()Conclusion
The Django database API simplifies working with databases by allowing developers to interact with data through Python code instead of raw SQL.
With methods like get(), filter(), and create(), you can easily query, update, and manage data in your models.
The relationships between models, such as the connection between Question and Choice, are handled seamlessly through Django’s ORM, enabling intuitive navigation and manipulation of related data.
Why the Database API is Helpful
1. Ease of Use: The API abstracts the complexities of SQL, making it easy for developers to interact with the database using Python code.
2. Efficiency: It provides built-in methods for common database operations, such as retrieving, filtering, updating, and deleting records.
3. Data Relationships: Django automatically manages relationships between models, allowing you to access related data effortlessly.
4. Scalability: The API supports complex lookups and operations, ensuring it can handle the needs of both small projects and large-scale applications.
5. Consistency: By working directly with models, you maintain consistency between your application’s logic and its underlying database structure.
In summary, Django’s database API empowers developers to focus on building features rather than managing database queries, enhancing productivity and ensuring cleaner, more maintainable code.
