Django for ISchoolers: Difference between revisions

Content added Content deleted
imported>Aldeka
No edit summary
imported>Aldeka
Line 523: Line 523:
* Import the model classes we just wrote:
* Import the model classes we just wrote:


<code>
<code>>>> from qandabear.models import Question, Answer</code>
>>> from qandabear.models import Question, Answer</code>


* List all the current Questions:
* List all the current Questions:


<code>
<code>>>> Question.objects.all()<br>
>>> Question.objects.all()<br>
[]</code>
[]</code>


How many questions is this?
How many questions is this?

Zen koan: Can there be an Answer for a Question that doesn’t yet exist?
Zen koan: Can there be an Answer for a Question that doesn’t yet exist?


=== Add our first Question ===
=== Add our first Question ===


<code>>>> import datetime<br>
<code>
>>> q = Question(text="What is the Weirdest Cookbook Ever?")
>>> q = Question(text="What is the Weirdest Cookbook Ever?")
# We could specify the value for pub_date as well here, but we don't have to since it has a default value (of when the question is created) set in its model definition</code>
# We could specify the value for pub_date as well here, but we don't have to since when we wrote its model definition, we specified that pub_date has a default value (of when the question is created)</code>

* Try getting the ID number of our new question 'q' by typing <code>q.id</code>. What happens?


* Save the Question instance into the database. You have to call save() explicitly.
* Save the Question instance into the database. You have to call save() explicitly.


<code>>>> q.save()</code>
<code>
>>> q.save()</code>


* Get the id of the Question instance. Because it’s been saved, it has an ID in the database.
* Get the id of the Question instance. Because it’s been saved, it has an ID in the database now!


<code>>>> q.id
<code>
>>> q.id
1</code>
1</code>


* Access the database columns (Fields, in Django parlance) as Python attributes:
* Access the database columns (Fields, in Django parlance) as Python attributes:


<code>>>> q.text<br>
<code>
>>> q.text
"What is the Weirdest Cookbook Ever?"<br>
"What is the Weirdest Cookbook Ever?"
>>> q.pub_date<br>
>>> q.pub_date
datetime.datetime(2007, 7, 15, 12, 00, 53)</code>
datetime.datetime(2011, 12, 1, 3, 3, 55, 841929)
</code>


* Send the Question back in time:
* Send the Question back in time:


<code># Change values by changing the attributes, then calling save().<br>
<code># Change values by changing the attributes, then calling save().<br>
>>> q.pub_date = datetime.datetime(2007, 4, 1, 0, 0)<br>
>>> import datetime<br>
>>> q.pub_date = datetime.datetime(2011, 4, 1, 0, 0)<br>
>>> q.save()<br>
>>> q.save()<br>
>>> q.pub_date<br>
>>> q.pub_date<br>
Line 581: Line 590:
# ...
# ...
def __unicode__(self):
def __unicode__(self):
# return the answer text, or if the text is longer than 37 characters, return a summary with an ellipsis
summary = self.answer[:37]
if len(self.answer) > 37:
summary = self.text[:37]
if len(self.text) > 37:
summary = summary + '...'
summary = summary + '...'
return summary</code>
return summary</code>


It’s important to add __unicode__() methods to your models, not only for your own sanity when dealing with the interactive prompt, but also because objects’ representations are used throughout Django’s automatically-generated admin.
It’s important to add __unicode__() methods to your models, not only for your own sanity when dealing with the interactive prompt, but also because if you use Django's automatically-generated admin, these representations are used there too.

(If you’re using to Python programming from a time in the past, you might have seen __str__(). Django prefers you use __unicode__() instead.)


=== Adding custom methods to models ===
=== Adding custom methods to models ===


Enough of these normal python methods! Let's build in some useful functionality to our models.
Enough of these normal python methods! Let's build in some useful functionality to our models in qandabear/models.py.


<code>import datetime
<code>import datetime
Line 601: Line 609:
return self.pub_date.date() == datetime.date.today()</code>
return self.pub_date.date() == datetime.date.today()</code>


Note the addition of import datetime to reference Python’s standard datetime module. This allows us to use the datetime library module in models.py by calling it with datetime.
Note the addition of <code>import datetime</code> to reference Python’s standard datetime module. This allows us to use the datetime library module in models.py by calling it with datetime.


* Save these changes to the models.py file.
* Save these changes to the models.py file.
Line 609: Line 617:
Start a new Python interactive shell by running python manage.py shell:
Start a new Python interactive shell by running python manage.py shell:


<code>
>>> from polls.models import Poll, Choice
>>> from qandabear.models import Question, Answer
Verify our __unicode__() addition worked:
Verify our __unicode__() addition worked:
>>> Poll.objects.all()
>>> Question.objects.all()
[<Question: What is the Weirdest Cookbook Ever?>]
Search your database using the filter method on the objects attribute of Question.
>>> questions = Question.objects.filter(text="What is the Weirdest Cookbook Ever?")
>>> questions
[<Poll: What is the Weirdest Cookbook Ever?>]
[<Poll: What is the Weirdest Cookbook Ever?>]
>>> questions[0].id # remember python lists start with element 0.
Search your database using the filter method on the objects attribute of Poll.
>>> polls = Poll.objects.filter(question="What is the Weirdest Cookbook Ever?")
>>> polls
[<Poll: What is the Weirdest Cookbook Ever?>]
>>> polls[0].id # remember python lists start with element 0.
1
1
</code>
If you try to search for a poll that does not exist, filter will give you the empty list. The get method will always return one hit, or raise an exception.
>>> Poll.objects.filter(question="What is the Weirdest Cookbook Ever?")
[]


If you try to search for a question that does not exist, filter will give you the empty list. The get method will always return one hit, or raise an exception.
>>> Poll.objects.get(id=1)

<Poll: What is the Weirdest Cookbook Ever?>
<code>
>>> Poll.objects.get(id=2)
>>> Question.objects.filter(text="Who framed Roger Rabbit?")
[]
>>> Question.objects.get(id=1)
<Question: What is the Weirdest Cookbook Ever?>
>>> Question.objects.get(id=2)
Traceback (most recent call last):
Traceback (most recent call last):
...
...
DoesNotExist: Poll matching query does not exist.
DoesNotExist: Question matching query does not exist.
</code>
Add Choices


===Add Answers===
Observe, there is a Poll in the database, but it has no Choices.

>>> p = Poll.objects.get(id=1)
Observe: there is a Question in the database, but it has no Answers! Let's fix that.
>>> p.choice_set.all()

<code>
>>> q = Question.objects.get(id=1)
>>> q.answer_set.all()
[]
[]
Create three choices:
Create three answers:
>>> p.choice_set.create(choice='To Serve Man', votes=0)
>>> q.answer_set.create(text='To Serve Man')
<Choice: To Serve Man>
<Answer: To Serve Man>
>>> p.choice_set.create(choice='The Original Road Kill Cookbook', votes=0)
>>> q.answer_set.create(text='The Original Road Kill Cookbook')
<Choice: The Original Road Kill Cookbook>
<Answer: The Original Road Kill Cookbook>
>>> c = p.choice_set.create(choice='Mini-Mart A La Carte', votes=0)
>>> a = q.answer_set.create(text='Mini-Mart A La Carte')
>>> c
>>> a
<Choice: Mini-Mart A La Carte>
<Answer: Mini-Mart A La Carte>
</code>
Go in reverse! Find the poll a particular choice belongs to:

>>> c.poll
Go in reverse! Find the question a particular answer belongs to:
<Poll: What is the Weirdest Cookbook Ever?>

Because a Poll can have more than one Choice, Django creates the choice_set attribute on each Poll. You can use that to look at the list of available Choices, or to create them.
<code>
>>> p.choice_set.all()
>>> a.question
[<Choice: To Serve Man>, <Choice: The Original Road Kill Cookbook>, <Choice: Mini-Mart A La Carte>]
<Question: What is the Weirdest Cookbook Ever?>
>>> p.choice_set.count()
</code>

Because a Question can have more than one Answer, Django creates the answer_set attribute on each Question. You can use that to look at the list of available Answer, or to create them.

<code>
>>> q.answer_set.all()
[<Answer: To Serve Man>, <Answer: The Original Road Kill Cookbook>, <Answer: Mini-Mart A La Carte>]
>>> q.answer_set.count()
3
3
</code>
No really. Can one be a Choice for a Poll that doesn’t yet exist?:

>>> koan = choice("Is this even a choice")
Can one be a Answer for a Question that doesn’t yet exist?:
>>> koan.poll_id

>>> koan.poll
<code>
>>> koan = Answer("Is this even an answer")
>>> koan.question_id
>>> koan.question
</code>

<!--- Adding pre-written data would go here. -->

=== Changing your models ===

Oh no! Our client, LulzTech, has decided to change the spec for this prototype. Two changes, in fact. First, they've decided that tracking the pub_date of Questions is silly, and they want to remove that field. Second, they want to add a votes field to each Answer, to track how many upvotes it has. Which means we’re going to have to change our models.

* Open qandabear/models.py and edit the Question class:

<code>
class Question(models.Model):
question = models.CharField(max_length=200)
</code>

* Edit the Answer class too:

<code>
class Answer(models.Model):
question = models.ForeignKey(Question)
text = models.TextField()
votes = models.IntegerField(default=0)
pub_date = models.DateTimeField(auto_now_add=True)
</code>

The default parameter lets us set a default value for this field if an answer's vote count isn't explicitly specified. Most new answers are going to have zero votes, so we set our default to 0.

* Make a migration so the database knows we deleted Question.pub_date and added Answer.votes:

<code>
$ python manage.py schemamigration qandabear --auto
</code>

* Apply the migration.

<code>
$ python manage.py migrate qandabear
</code>

===Save and commit===


You know the drill!


== Database migrations and South, part two ==
== Database migrations and South, part two ==