I have a UserProfile model with related_name='profile' for the User FK.
Let's say I have a User obj, user1. If I want to get the UserProfile object from the user1, what is the difference between using user1.profile.all() and user1.get_profile() in terms of db hits and efficiency?
Neither of these commands is actually valid in Django. However, if you fix the syntax issues, they do completely different things.
If you want to get both the User instance and its associated Profile in one go, with a single db hit, you would use this:
user = User.objects.select_related('profile').get(pk=my_pk_value)
Now you can access the profile from the user by doing user.profile, and you don't incur another db hit. You can do exactly the same if you miss out select_related, but it will incur another db hit.
If you already have a User object user, you would do user.get_profile(), and that gets you the actual Profile object - with another db hit.
Related
Hey guys I am new to django and have a doubt regarding using these expressions a lot.
blog.user.username or blog.user.email, etc. Does this execute a database operation like we call User.objects.get(username=blog.user.username) to fetch that info? or is it preloaded when we load the blog instance object, i mean along with this get_object_or_404(Blog, id=id)?
Django loads relations lazily: that means that if you load a blog object, it will not load user immediately, and thus only if you later access .user, it will load the user in the memory. If you for example use blog.user.profile, then it will make queries for blog, its user and its profile.
You can however boost efficiency with .select_related(…) [Django-doc]. Indeed, if we work with:
get_object_or_404(
Blog.objects.select_related('user', 'user__profile'),
pk=id
)
it will load the related user and profile in the same query, and thus this will take only one query, and the .user and .user.profile data are loaded with this database query.
In Django, when you create an object, either with a Django helper function like 'create_user' or with an object's 'create' method, Django automatically creates an auto-incrementing primary key for that new object:
user = User.objects.create_user(username='foo', password='bar')
user.save() # id = 1
account = Account.objects.create(user=user)
account.save() # id = 1
But in the real world, if your server crashes and you have to rebuild your database server and then reload all your data, the first time you try to create a new object, you're going to get this type of error:
django.db.utils.IntegrityError: duplicate key value violates unique constraint "new-object_pkey"
Whenever you create a new model instance, Django seems to assume you're starting with a clean, new database and it assigns the new object an id of 1 even though, if you had to restore the database, you already have an object in the restored table with an id of 1.
What is the "Django way" to avoid this situation with both Django's helper functions as well as the object "create" method? I would think that this would be a common problem for any production Django website and that Django would recognize this and handle it automatically. But I don't see such a solution discussed anywhere. How do you avoid this situation?
UPDATE
I've discovered the solution. Django does not select primary keys, your database does. In the situation I describe, you need to run Django's 'sqlsequencereset' command against your models. I'll answer my own question in a couple of days to detail how I did this.
I have an authentication backend based off a legacy database. When someone logs in using that database and there isn't a corresponding User record, I create one. What I'm wondering is if there is some way to alert the Django system to this fact, so that for example I can redirect the brand-new user to a different page.
The only thing I can think of is adding a flag to the users' profile record called something like is_new which is tested once and then set to False as soon as they're redirected.
Basically, I'm wondering if someone else has figured this out so I don't have to reinvent the wheel.
I found the easiest way to accomplish this is to do exactly as you've said. I had a similar requirement on one of my projects. We needed to show a "Don't forget to update your profile" message to any new member until they had visit their profile for the first time. The client wanted it quickly so we added a 'visited_profile' field to the User's profile and defaulted that to False.
We settled on this because it was super fast to implement, didn't require tinkering with the registration process, worked with existing users, and didn't require extra queries every page load (since the user and user profile is retrieved on every page already). Took us all of 10 minutes to add the field, run the South migration and put an if tag into the template.
There's two methods that I know of to determine if an object has been created:
1) When using get_or_create a tuple is returned of the form (obj, created) where created is a boolean indicating obviously enough whether the object was created or not
2) The post_save signal passes a created paramater, also a boolean, also indicating whether the object was created or not.
At the simplest level, you can use either of these two hooks to set a session var, that you can then check and redirect accordingly.
If you can get by with it, you could also directly redirect either after calling get_or_create or in the post_save signal.
You can use a file-based cache to store the users that aren't yet saved to the database. When the user logs in for the second time, you can look in the cache, find the user object, and save it to the database for good.
Here's some info on django caching: http://docs.djangoproject.com/en/dev/topics/cache/?from=olddocs
PS: don't use Memcached because it will delete all information in the situation of a computer crash or shut down.
I have a Django app that works well for me, but currently has no notion of user: I am the only one using it, and I would like to change this...
Except for the admin views, the logged-in user should not have access to the data created by other users. There is no shared data between users.
I suppose I have to add a user foreign key to all the models I created. Correct?
Is there a simple way to implement the filtering based on request.user? Can this be done more or less automatically, or do I have to go through all the code to check each and every query done on the database?
I have written the code using TDD, and I intend to follow up... What are the best strategies to ensure that user-filtering is implemented correctly, e.g. that I did not forget to filter an existing query? I suppose I can write tests that show that a particular query is not yet filtered, and implement the filter. But what about the queries that I will write later? Is there a way I can assert that all existing and future queries return objects that only belong to the current user?
Thanks.
Yes, you'll need to add a User FK. Don't forget you'll have to migrate your database tables - either manually, or via a tool like South.
One way of implementing the filter would be to define custom Managers for your models, with a for_user method that takes the User as an argument: something like:
class ForUserManager(models.Manager):
def for_user(self, user):
return self.filter(user=user)
Now you can use this manager - subclassed and/or with a mixin as necessary - on all your models, and remember to use objects.for_user(request.user) everywhere.
This will make testing easier too - your test could monkeypatch that for_user method so that it sets a flag or a counter in a global variable somewhere, and then test that it has incremented as expected.
Edit in response to comment No, as you suspect, that won't work. It's not even that everyone will necessarily get the last-logged-in user: it's that Managers are class-level attributes, and as such are reused throughout a process, so any request served by that server process will use the same one.
I am using Django admin for managing my data. I have the following tables: Users, Groups, and Domains. Users has a many-to-many relationship with both Groups and Domains. Domains has a one-to-many relationship with Groups. When I remove a User from a Domain, I also want to remove any entries in Users_Groups for that particular User and Groups belonging to the Domain.
How do I do this? Where do I put the code?
Thanks.
The Django book (specifically, Appendix B) seems to suggest you override the delete method on the User model class and have it trigger the extra deletes.
I would suggest overriding save, but I'm guessing you're using the django.contrib.auth.User object. In that case, you can accomplish about the same thing with a pre_save signal:
def manage_domains(signal,**kwargs):
if kwargs.has_key('instance'):
instance = kwargs['instance']
else: return
old_instance = User.objects.get(pk=instance.pk)
instance_categories = instance.categories.all()
for group in old_instance.groups.all():
if group not in instance_categories:
instance.groups.clear()
pre_save.connect(manage_domains, sender=User)
This is not even close to an efficient solution. What will happen is that when a User object is saved, the above changes will be made to the object in memory, then the save will overwrite the object in the database. You've gone to the trouble not only of hitting the database to find out if the unmodified version of the object agrees with with what you're about to save, but you're also looping through two category sets, both requiring a database hit to load.
The best way to make an efficiency improvement here would be to subclass the ManyToMany field manager itself, because that's what's paying attention to changes as they occur in memory, rather than merely comparing state after the fact. But this would be a bit more involved and would also require you to abandon the built-in User object.