How can i keep my database clean between my scenarios? - django

I have two scenarios that create a record in a database. Let's say I'm creating a user with foo#bar.com.
Scenario 1: Given I have an user with email foo#bar.com
Scenario 2: Given I have an user with email foo#bar.com
Scenario 2 is raising an error that says there is already a record with foo#bar.com. I think that I need to setup a hook to cleanup database between scenarios.
What is best practice for this stuation? Calling flushdb command between scenarios? Or transaction rollbacks? Or what else?

Use the get_or_create method in both scenarios. Don't forget that get_or_create returns a tuple:
>>> from django.contrib.auth.models import User
>>> superman, created = User.objects.get_or_create(username='kal-el')
>>> superman
<User: kal-el>
>>> created
True
>>> superman_returns, created_again = User.objects.get_or_create(username='kal-el')
>>> superman_returns
<User: kal-el>
>>> created_again
False
https://docs.djangoproject.com/en/1.8/ref/models/querysets/#get-or-create

Related

Django cascade delete doesn't remove pk of referenced model instance

In Django 1.9 I have related objects defined in models.py as follows:
from django.db import models
class Course(models.Model):
title = models.CharField(max_length=10)
class Note(models.Model):
course = models.ForeignKey(Course)
When I delete a Course, I expect all related Note s to delete via the default cascade behaviour. What I get is the following behaviour:
>>> from test1.models import Course, Note
#First, create a Course and associated Note
>>> c1 = Course()
>>> c1.save()
>>> n1 = Note(course=c1)
>>> n1.save()
>>> c1.pk
4
>>> n1.pk
4
#Next, delete the Course, and see if Note deletes too
>>> c1.delete()
(2, {'test1.Course': 1, 'test1.Note': 1})
>>> c1.pk
>>> n1.pk
1 #pk remains
>>> Note.objects.get(pk=4)
Traceback (most recent call last):
... test1.models.DoesNotExist: Note matching query does not exist.
#Finally, try deleting the Note manually
>>> n1.delete()
(0, {'test1.Note': 0})
>>> n1.pk
>>> #finally the pk is gone!
It seems that the database is correctly updated but only the Course object is updated locally, while the Note object is not (i.e. keeps its pk).
Why is this, and how can I get Note to also delete pk so that it's in sync with the db?
Extra info: My reason for needing this behaviour is that I have used statements like if note.pk: elsewhere to check if a given Note is already saved in the database. Unfortunately, this style of cascade delete renders these statements useless because pk's exist even when db entries have been deleted.
This is expected behaviour. Once Django has fetched something from the database (n1 in this case) it isn't going to know about changes to it until you fetch it again (either with Note.objects.get(pk=4) or n1.refresh_from_db().
When you call c1.delete(), Django deletes that row from the database and clears the pk on the object. It also deletes all related objects in the database that are set to cascade. However it has no way of knowing what other already-instantiated objects are referencing that object - how could it?
Here is a simpler example of the same behaviour, without foreign keys:
# Fetch a Course
>>> c1 = Course.objects.get(pk=1)
# Fetch the same course again, but save it to a different variable
>>> c2 = Course.objects.get(pk=1)
Note that you now have two instances of the same row in the database. Now:
# Delete c1
>>> c1.delete()
# c2 Hasn't changed... how could it? That would be magical.
>>> c2.pk
1
# but if you refresh c2 from the database... it isn't there
>>> c2.refresh_from_db()
DoesNotExist: Course matching query does not exist.

django can i use prefetch_related on a evaluated queryset without redoing the initial query

consider this code
users = UserProfile.objects.all()[:10]
# evaluate the query set
users_list = list(users)
users = users.prefetch_related('posts')
i want to know that using prefetch_related on queryset after it is evaluated will repeat the query on UserProfile model or not.
Thanks.
No. Until you evaluate it again, as it cannot magically pull the extra data from the database.
>>> from django.db import connection
>>> from app.models import Foo
>>> bar = Foo.objects.all()[:1]
>>> len(connection.queries)
0
>>> bar_list = list(bar)
>>> len(connection.queries)
1
>>> bar = bar.prefetch_related('thing')
>>> len(connection.queries)
1
>>> bar_list = list(bar)
>>> len(connection.queries)
2
No, in this scenario, the query will hit only when you call the variable users.
print users
Hit:
(0.000) QUERY = 'SELECT “userprofile"."id" INNER JOIN “posts" ON ( “userprofile"."usuari...
As explained here, the QuerySet API executes the database query at the first time you iterate over it. In your case the iteration happens because of the list(users).
The prefetch_related function would have an effect on the first query if you would call it before execution.
So, that means: Yes, you can call prefetch_related after iteration, but the QuerySet has to do a new database query to get the missing information about the posts. QuerySets are cloned every time you call a function like prefetch_related. So, the next iteration is the first one for the cloned object.

Using django fields validation for external values

I have model with one field(this is synthetic example):
model Tank:
oxygen = models.PositiveSmallIntegerField(
_("Oxygen %"),
help_text="%",
default=21,
validators=[MinValueValidator(21.0), MaxValueValidator(50.0)],
null=True,
)
And I parse some files with data. I want to validate input data before write it model instance. Something like this
oxygen = get_raw_data()
Tank.oxygen.validate(oxygen) # this is wrong I know :)
# if value is valid do something
# else do something
What should I write instead of Tank.oxygen.validate(oxygen)?
I can duplicate validation logic or validate data when save model instance, but maybe somebody know better solution.
You need to actually create an instance with the data, then call full_clean() on it:
my_tank = Tank(oxygen=oxygen)
my_tank.full_clean()
If you only want to validate one field, then I suggest you use a form.Field class to do it:
from django import forms
from django.core.exceptions import ValidationError
oxygen_field = forms.IntegerField(required=True, min_value=21, max_value=50)
def is_oxygen_valid(value):
try:
oxygen_field.clean(value)
except ValidationError:
return False
else:
return True
Testing:
>>> is_oxygen_valid(None)
False
>>> is_oxygen_valid(11)
False
>>> is_oxygen_valid(55)
False
>>> is_oxygen_valid('hello')
False
>>> is_oxygen_valid(list())
False
>>> is_oxygen_valid(45)
True
I'm assuming that you are going to be validating the oxygen field first, and then deciding what to do depending on the result. You could adapt this to any other field you need. Just find the appropriate FormField class, and any Validators you might need, and use that instead.

Django Database querying differences

I am going through the creation of the Polls app again, in the Django Docs. I wanted to ask again about one particular thing they do in the django database. The code is shown below:
>>> from polls.models import Poll, Choice
# Make sure our __unicode__() addition worked.
>>> Poll.objects.all()
[<Poll: What's up?>]
# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Poll.objects.filter(id=1)
[<Poll: What's up?>]
>>> Poll.objects.filter(question__startswith='What')
[<Poll: What's up?>]
# Get the poll that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Poll.objects.get(pub_date__year=current_year)
<Poll: What's up?>
# Request an ID that doesn't exist, this will raise an exception.
>>> Poll.objects.get(id=2)
Traceback (most recent call last):
...
DoesNotExist: Poll matching query does not exist. Lookup parameters were {'id': 2}
# 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 Poll.objects.get(id=1).
>>> Poll.objects.get(pk=1)
<Poll: What's up?>
# Make sure our custom method worked.
>>> p = Poll.objects.get(pk=1)
>>> p.was_published_recently()
True
# Give the Poll 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 to hold the "other side" of a ForeignKey relation
# (e.g. a poll's choices) which can be accessed via the API.
>>> p = Poll.objects.get(pk=1)
# Display any choices from the related object set -- none so far.
>>> p.choice_set.all()
[]
# Create three choices.
>>> p.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> p.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = p.choice_set.create(choice_text='Just hacking again', votes=0)
# Choice objects have API access to their related Poll objects.
>>> c.poll
<Poll: What's up?>
If you take a look at the variable c, you will see that it is created using this, p.choice_set.create(choice_text='Just hacking again', votes=0). Now if you created it instead with this: c = p.choice_set.filter(id=3), and when you type in c.poll, it will give you an error. Why does this happen? The console gives me this error : AttributeError: 'Poll' object has no attribute 'create', but I do not understand what it means.
Also, is there any way of getting c.poll to give you an output without having to create a new choice?
--
Thanks in advance
c = p.choice_set.filter(id=3) won't return a single choice object. It returns a queryset composed of a single choice object because, obviously, there is just one object with the same id. Querysets are iterables, which means that if you want to obtain the choice object from that variable it should be: c = p.choice_set.filter(id=3)[0]
That is the difference with choice_set.create: create returns the single created object.
Now, that's not the way to do it. When you know you are querying for a single object, use get.
c = p.choice_set.get(id=3).
See querying documentation for further details.

How can I change user group without delete record

On my website any user have only one group. And any user can change his group.
So it's made by
user.groups.clear()
and
user.groups.add(new_group)
But it's not efficient, because there is a two SQL query: DELETE, INSERT.
How can I change group by just UPDATE query?
User and Group are related to each other using a ManyToManyField. That means an intersection table exists relating both entities, and if you don't specify a model to map to it (using the through attribute) Django creates one for you. Looking at the sources for django.contrib.auth.models I see that's the case.
Fortunatly, you can access that intermediary model using the through attribute of the manager (in this case, User.groups.through). Then you can use it just like any regular model. Example:
>>> alice = User.objects.create_user('alice', 'alice#example.com', 'alicepw')
>>> employee = Group.objects.create(name='employee')
>>> manager = Group.objects.create(name='manager')
>>> alice.groups.add(employee)
>>> alice.groups.all()
[<Group: employee>]
>>> alice_group = User.groups.through.objects.get(user=alice)
>>> alice_group
<User_groups: User_groups object>
>>> alice_group.group = manager
>>> alice_group.save()
>>> alice.groups.all()
[<Group: manager>]
>>>
(newlines added for readability)