Right now I'm using Django's built in admin system to manage users, to which I've attached a profile to contain additional data using the following:
class Profile(models.Model):
user = models.OneToOneField(User, editable = False)
# Data fields here...
As it stands the User and Profile pk (and accordingly id number) will be the same if and only if the profile is created right after the user is created. I could guarantee that this would be the case during the registration process, and while that would cover most uses, creating users with the admin interface could cause mismatched ids to occur. Thus this does not seem like a very robust way to solve this problem and I'd like to hardcode the pk's to be the same. I'm not sure how to do this.
I thought the following would work:
profile_id = models.IntegerField(default=user.pk, editable = False,
primary_key = True)
But it gives me the error:
AttributeError: 'OneToOneField' has no attribute 'pk'
What's the best way to guarantee that the profile and user have the same pk? Note: I'd really rather not deal with extending the base user model as using the OneToOneField to link the two seems to be sufficient for all my needs.
Thanks!
[edit]
My reasoning for asking the question:
My immediate problem was that I wanted a dictionary of values of the User's Profile, which I was retrieving usingprofile_values = Profile.objects.filter(pk=user.id).values()[0]. This highlighted the bug, and I "hacked" around it last night using pk=user.profile.id instead. In the light of the morning this does not seem like such a terrible hack. However, it seems like having pk discrepancies could lead to quiet and hard to catch bugs down the line, and thus forcing them to match up would be a Good Idea. But I'm new to Django so I'd entirely accept that it is, in fact, never a problem if you're writing your code correctly. That said, for almost academic reasons, I'd be curious to see how this might be solved.
[/edit]
Like you already agree that it was never a problem because we have a OneToOne mapping between the two models.
So when you need to get the profile obj corresponding to a User:
profile_values = Profile.objects.get(user_id=user)
assuming,
class Profile(models.Model):
user = models.OneToOneField(User)
...
If your column name is not user, then use the corresponding name in get query.
Still if you are curious as to how to achieve same pk for both models, then we can set a signal on every save of User model. See the documentation.
def create_profile(sender, **kwargs):
if kwargs["created"]:
p = Profile(user=kwargs["instance"], ...)
p.save()
django.db.models.signals.post_save.connect(create_profile, sender=User)
create_profile() will be called every time any User object is saved.
In this function, we create Profile object only if a new User instance has been created.
If we start from blank slate, then I think this will always make sure that a Profile exists for every User and is created right after User was created; which in turn will give same pk for both models.
pk is a parameter in a filter() query, but not a field name. You probably want to use user.id.
Related
I am trying to create a model with a foreign key which references the django model User.
I have found that you are supposed to use this model when creating something like this:
author = models.ForeignKey(User)
however any time i try to assign this from my view with this line:
if form.is_valid():
c = form.save(commit=False)
c.author=request.user.id
c.save()
I get an error complaining about how author should come from the model User. There is no User property (at least that i (a noob working on their first django project) could find) which has the user id. what is the preferred method for linking a post to it's author in django? am i going about this completely the wrong way? is there a better way of solving this problem that i am just not thinking of?
You just assign this only not id,
c.author=request.user
For assigning user, it should be like:
c.author= User.objects.filter(id= request.user.id)[0]
Up until recently, a project I'm working used one mega UserProfile to handle all profile data for two different types of users. Naturally this was messy, and it was about time to refactor it.
In my attempt to refactor the model, I split the model into Requester and Funder and created an abstract UserProfile model which both subclass:
class UserProfile(models.Model):
class Meta:
abstract = True
user = models.OneToOneField(User)
def __unicode__(self):
return unicode(self.user)
class Requester(UserProfile):
def requested(self, event):
"""Check if a user requested an event."""
return self == event.requester
class Funder(UserProfile):
osa_email = models.EmailField(null=True) # The e-mail of the contact in OSA
mission_statement = models.TextField(max_length=256)
And in my settings.py file, I adjusted the AUTH_PROFILE_MODULE.
AUTH_PROFILE_MODULE = "app.UserProfile"
The problem is, when hitting a page that uses "User.get_profile()" it breaks, reporting:
Unable to load the profile model, check AUTH_PROFILE_MODULE in your project settings
I'm not quite sure what's going on here. According to the docs, everything looks right.
Can some explain why this fails? (There are a bunch of alternative solutions I've come across, but I'd much prefer to fix this if possible than adopt some hack.)
What you are trying to do it not possible. AUTH_PROFILE_MODULE is expecting a concrete model, not an abstract one. Concrete means it has a table and can create instances. An abstract model can only be subclassed.
A logic reason why this not possible it that django has no one of knowing which model instance to return for your user. A Requester? A Funder? Simply being an abstract reference gives django no hints. One approach might be to look into the contenttypes framework and maybe come up with a generic UserProfile model containing a reference to the proper sub-profile type. You could then remove the abstract=True from your UserProfile, and create a generic relation to the specific Profile model. AUTH_PROFILE_MODULE would then simply reference that single UserProfile, but its instances can then use the .content_object to get the specific subobject.
There are many ways I'm sure you could address this problem, but I am just commenting on the reason why this specific approach does not work.
So I've got a UserProfile in Django that has certain fields that are required by the entire project - birthday, residence, etc. - and it also contains a lot of information that doesn't actually have any importance as far as logic goes - hometown, about me, etc. I'm trying to make my project a bit more flexible and applicable to more situations than my own, and I'd like to make it so that administrators of a project instance can add any fields they like to a UserProfile without having to directly modify the model. That is, I'd like an administrator of a new instance to be able to create new attributes of a user on the fly based on their specific needs. Due to the nature of the ORM, is this possible?
Well a simple solution is to create a new model called UserAttribute that has a key and a value, and link it to the UserProfile. Then you can use it as an inline in the django-admin. This would allow you to add as many new attributes to a UserProfile as you like, all through the admin:
models.py
class UserAttribute(models.Model):
key = models.CharField(max_length=100, help_text="i.e. Age, Name etc")
value = models.TextField(max_length=1000)
profile = models.ForeignKey(UserProfile)
admin.py
class UserAttributeInline(admin.StackedInline):
model = UserAttribute
class UserProfile(admin.ModelAdmin):
inlines = [UserAttibuteInline,]
This would allow an administrator to add a long list of attributes. The limitations are that you cant's do any validation on the input(outside of making sure that it's valid text), you are also limited to attributes that can be described in plain english (i.e. you won't be able to perform much login on them) and you won't really be able to compare attributes between UserProfiles (without a lot of Database hits anyway)
You can store additional data in serialized state. This can save you some DB hits and simplify your database structure a bit. May be the best option if you plan to use the data just for display purposes.
Example implementation (not tested)::
import yaml
from django.db import models
class UserProfile(models.Model):
user = models.OneToOneField('auth.User', related_name='profile')
_additional_info = models.TextField(default="", blank=True)
#property
def additional_info(self):
return yaml.load(self._additional_info)
#additional_info.setter
def additional_info(self, user_info_dict):
self._additional_info = yaml.dump(user_info_dict)
When you assign to profile.additional_info, say, a dictionary, it gets serialized and stored in _additional_info instead (don't forget to save the instance later). And then, when you access additional_info, you get that python dictionary.
I guess, you can also write a custom field to deal with this.
UPDATE (based on your comment):
So it appears that the actual problem here is how to automatically create and validate forms for user profiles. (It remains regardless on whether you go with serialized options or complex data structure.)
And since you can create dynamic forms without much trouble[1], then the main question is how to validate them.
Thinking about it... Administrator will have to specify validators (or field type) for each custom field anyway, right? So you'll need some kind of a configuration option—say,
CUSTOM_PROFILE_FIELDS = (
{
'name': 'user_ip',
'validators': ['django.core.validators.validate_ipv4_address'],
},
)
And then, when you're initializing the form, you define fields with their validators according to this setting.
[1] See also this post by Jacob Kaplan-Moss on dynamic form generation. It doesn't deal with validation, though.
Here is the sutuation i hit.
I have both User and ProfileUser. I would like to add additional logic to the model and since I can't add it to the Django User model, I have to add it to the ProfileUser. Currently all my models however have ForeignKey(User). Should I keep them like that or should I user ForeignKey(UserProfile) on my other models?
Example for my view if I keep the ForeignKey(User):
class myview(request):
user = request.user
userProfile = user.get_profile()
neededStuff = userProfile.get_needed_stuff()
and then in the UserProfile model:
def get_needed_stuff(self):
user= self.user # Or actually, is this right
goals = Goal.objects.get(<conditions that i wont bother writing here>)
return goals
So for this case, and for further development of the site, which foreign key should i use?
I think You should use User. UserProfile should be custom and can differ on each project. So if you will use same code for another project you can probably fail because of that. Also it is always easy to get user object in code and from that you have no problems to get profile user.get_profile() as you show (and profile is not always needed). So ingeneral I think it will be easier to use other modules and passing them just user object (or id) and not the profile.
What is also could be the solution - write your own class which will be responsible for the users. Just write methods to return profile, return stuff_needed or whatever you want and everything just by passing user object and additional parameters about what you want.
So in short, I'm for using User for Foreign keys, because in my opinion it just more logical, while the User model is always the main one (you always have it) and UserProfile is just extension.
Ignas
If you just want all the goals belonging to a specific user add a foreign key to User in your Goal model.
class Goal(models.Model):
user = models.ForeignKey(User)
def myview(request):
goals = Goal.objects.filter(user=request.user)
Or alternately save all the goals for a user on your UserProfile model and do
def myview(request):
user_profile = user.get_profile()
goals = user_profile.goals
...or use a method to do processing to calculate them
goals = user_profile.calculate_goals()
I've been pondering the same thing myself for one of my sites but i decided to use UserProfile rather than User.
Not sure if its the right decision but it just seems more flexible.
Two questions please:
I need two foreign keys back to User, one for author and one for coauthor. I have managed by setting related_name equal to + but am not sure if this is smart or the best way. Thoughts or pointers?
When making an add entry via the django admin for this model, the author choices are names like john_smith, etc. Where would I call get_full_names() from in order to display full names rather than usernames with underscores? Thanks.
from django.contrib.auth.models import User
from django.db import models
class Books(models.Model):
title = models.CharField()
author = models.ForeignKey(User)
coauthor = models.ForeignKey(User, related_name='+')
Kevin
I would change the related name to a value that is more intelligible - such as books_where_coauthor and also add a similar one of books_where_author as then you can get the relevant books by going from theuser.books_where_author.all() etc
Regarding your Admin query, you're getting the username because that's what the default __unicode__() method of User spits out.
Unless you'd like to hack your contrib.auth.models file (not recommended), I'd suggest using a custom modelform in the admin, and manually setting the names of the choices in the ModelChoiceField, either by subclassing that field and making a custom one that renders its widget with get_full_name if possible, or do it via something like this snippet. That said, I am sure there's a simpler way to do that, but I've forgotten. Dangit.
With regard to the second part of my question (displaying full names instead of usernames in the ChoiceField of a form), I found this link to be just the ticket: