Marcin Kulus
Marcin Kulus

Reputation: 393

Django models - assign id instead of object

I apologize if my question turns out to be silly, but I'm rather new to Django, and I could not find an answer anywhere.

I have the following model:

class BlackListEntry(models.Model):
  user_banned = models.ForeignKey(auth.models.User,related_name="user_banned")
  user_banning = models.ForeignKey(auth.models.User,related_name="user_banning")

Now, when i try to create an object like this:

BlackListEntry.objects.create(user_banned=int(user_id),user_banning=int(banning_id))

I get a following error:

Cannot assign "1": "BlackListEntry.user_banned" must be a "User" instance.

Of course, if i replace it with something like this:

user_banned = User.objects.get(pk=user_id)
user_banning = User.objects.get(pk=banning_id)
BlackListEntry.objects.create(user_banned=user_banned,user_banning=user_banning)

everything works fine. The question is:

Does my solution hit the database to retrieve both users, and if yes, is it possible to avoid it, just passing ids?

Upvotes: 21

Views: 7091

Answers (2)

Francisco
Francisco

Reputation: 1382

The answer to your question is: YES.

Django will hit the database (at least) 3 times, 2 to retrieve the two User objects and a third one to commit your desired information. This will cause an absolutelly unnecessary overhead.

Just try:

BlackListEntry.objects.create(user_banned_id=int(user_id),user_banning_id=int(banning_id))

These is the default name pattern for the FK fields generated by Django ORM. This way you can set the information directly and avoid the queries.

If you wanted to query for the already saved BlackListEntry objects, you can navigate the attributes with a double underscore, like this:

BlackListEntry.objects.filter(user_banned__id=int(user_id),user_banning__id=int(banning_id))

This is how you access properties in Django querysets. with a double underscore. Then you can compare to the value of the attribute.

Though very similar, they work completely different. The first one sets an atribute directly while the second one is parsed by django, that splits it at the '__', and query the database the right way, being the second part the name of an attribute.

You can always compare user_banned and user_banning with the actual User objects, instead of their ids. But there is no use for this if you don't already have those objects with you.

Hope it helps.

Upvotes: 29

Dave
Dave

Reputation: 6179

I do believe that when you fetch the users, it is going to hit the db...

To avoid it, you would have to write the raw sql to do the update using method described here:

https://docs.djangoproject.com/en/dev/topics/db/sql/

If you decide to go that route keep in mind you are responsible for protecting yourself from sql injection attacks.

Another alternative would be to cache the user_banned and user_banning objects.

But in all likelihood, simply grabbing the users and creating the BlackListEntry won't cause you any noticeable performance problems. Caching or executing raw sql will only provide a small benefit. You're probably going to run into other issues before this becomes a problem.

Upvotes: 0

Related Questions