Django User set attribute and reverse relationships - django

Im using the below code in my views.py page to render a html page. This piece of code is from a Django book and im trying to understand the bookmark_set attribute.
views.py
def user_page(request, username):
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
raise Http404(u'Requested user not found')
bookmarks = user.bookmark_set.all()
template = get_template('user_page.html')
variables = Context({'username':username, 'bookmarks':bookmarks})
output = template.render(variables)
return HttpResponse(output)
models.py
from django.db import models
from django.contrib.auth.models import User
class Link(models.Model):
url = models.URLField(unique=True)
class Bookmark(models.Model):
title = models.CharField(max_length=200)
user = models.ForeignKey(User)
link = models.ForeignKey(Link)
When I run this piece of code in my Python shell, I get the following error
from django.contrib.auth.models import User
from bookmarks.models import *
user=User.object.get(id=1)
user.bookmark_set.all()
Attribute Error: 'User' object has no attribute 'bookmark_set'
Why do I get this error?
How does the set attribute of the User work?

The bookmark_set attribute provides a convenient way to traverse the reverse relationship, i.e. to get all of a user's bookmarks. You can read more about it in the docs:
Django also creates API accessors for the "other" side of the relationship -- the link from the related model to the model that defines the relationship. For example, a Blog object b has access to a list of all related Entry objects via the entry_set attribute: b.entry_set.all().
You can actually specify the name of the reverse relationship attribute by providing a related_name in your model:
class Bookmark(models.Model):
user = models.ForeignKey(User, related_name="bookmarks")
$ myuser.bookmarks.all()
Have you definitely created your models in the DB by running python manage.py syncdb? It looks like you are doing everything correctly

I don't know why, but add this; select_related() it worked in my case.
user=User.object.select_related().get(id=1)
user.bookmark_set.all()

Related

How do I set a default user for blog posts in Django?

My Blog model has a User field like following:
from django.contrib.auth.models import User
class Blog:
author = models.ForeignKey(User, on_delete = models.CASCADE, related_name='blog', default=User('monty'))
This works as in I can see 'monty' set as a default user in the admin interface when I create a blog post. However, when I make migrations, I get the following error:
ValueError: Cannot serialize: <User: >
There are some values Django cannot serialize into migration files.
I also tried this:
default=User.objects.filter(username='monty'))
and that returns a slightly different error when I make migrations:
ValueError: Cannot serialize: <User: monty>
There are some values Django cannot serialize into migration files.
Does anyone know how to get past this error?
You can make a callable that determines the User object, so:
from django.conf import settings
from django.contrib.auth import get_user_model
def get_monty():
if get_monty.user:
return user
user, __ = get_user_model().get_or_create(username='monty')
get_monty.user = user
return user
get_monty.user = None
class Blog(models.Model):
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='blog',
default=get_monty
)
That being said, I think it makes no sense to specify a default here. Your views can determine the logged in user and set that as the author. By using a default you likely will eventually end up with some Posts for which the view did not implement the logic, and are thus all assigned to monty, it
thus will silence an error that probably should not be silenced.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

A Django form for entering a 0 to n email addresses

I have a Django application with some fairly common models in it: UserProfile and Organization. A UserProfile or an Organization can both have 0 to n emails, so I have an Email model that has a GenericForeignKey. UserProfile and Organization Models both have a GenericRelation called emails that points back to the Email model (summary code provided below).
The question: what is the best way to provide an Organization form that allows a user to enter organization details including 0 to n email addresses?
My Organization create view is a Django class-based view. I'm leaning towards creating a dynamic form and an enabling it with Javascript to allow the user to add as many email addresses as necessary. I will render the form with django-crispy-forms and django-floppyforms for display on a site using Twitter Bootstrap.
I've thought about doing this with a BaseGenericInlineFormset embedded within the form, but this seems like overkill for email addresses. Embedding a formset in a form delivered by a class-based view is cumbersome too.
Note that the same issue occurs with the Organization fields phone_numbers and locations.
Code
emails.py:
from django.db import models
from parent_mixins import Parent_Mixin
class Email(Parent_Mixin,models.Model):
email_type = models.CharField(blank=True,max_length=100,null=True,default=None,verbose_name='Email Type')
email = models.EmailField()
class Meta:
app_label = 'core'
organizations.py:
from emails import Email
from locations import Location
from phone_numbers import Phone_Number
from django.contrib.contenttypes import generic
from django.db import models
class Organization(models.Model):
active = models.BooleanField()
duns_number = models.CharField(blank=True,default=None,null=True,max_length=9) # need to validate this
emails = generic.GenericRelation(Email,content_type_field='parent_type',object_id_field='parent_id')
legal_name = models.CharField(blank=True,default=None,null=True,max_length=200)
locations = generic.GenericRelation(Location,content_type_field='parent_type',object_id_field='parent_id')
name = models.CharField(blank=True,default=None,null=True,max_length=200)
organization_group = models.CharField(blank=True,default=None,null=True,max_length=200)
organization_type = models.CharField(blank=True,default=None,null=True,max_length=200)
phone_numbers = generic.GenericRelation(Phone_Number,content_type_field='parent_type',object_id_field='parent_id')
taxpayer_id_number = models.CharField(blank=True,default=None,null=True,max_length=9) # need to validate this
class Meta:
app_label = 'core'
parent_mixins.py
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.db import models
class Parent_Mixin(models.Model):
parent_type = models.ForeignKey(ContentType,blank=True,null=True)
parent_id = models.PositiveIntegerField(blank=True,null=True)
parent = generic.GenericForeignKey('parent_type', 'parent_id')
class Meta:
abstract = True
app_label = 'core'
you can try using .split(), with this your form would look easier and users wont have to keep on adding text fields.
What you can do is, make one textbox where user can add multiple emails and separate them by a coma. and then in your views you can do this
email = emails.split(',')
for i in emails:
#assign email and save.
in case of having an email type
it might still be a good idea to build a system like that. what you can do is
abc#gmail.com-work,xyz#k.com-school
and then you can split them like this
email-type=email.split(',')
for i in email-type:
email=i.split('-')[0]
if i.split('-')[1]:
type=i.split('-')[1]
else:
#give it a default type

Django: Issues with extending User model

I'm trying to add fields to the User model and add them to the admin page. There is a recommended method in the django docs here:
https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users
So, I created a OneToOne field for my new model:
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
designs = models.ManyToManyField('Design', blank=True)
prints = models.ManyToManyField('Print', blank=True)
rating = models.IntegerField(null=True, blank=True)
reliability = models.IntegerField(null=True, blank=True)
av_lead_time = models.IntegerField(null=True, blank=True)
Added an AUTH_PROFILE_MODULE to settings.py:
AUTH_PROFILE_MODULE = 'website.UserProfile'
Tried to add the UserProfile fields to the admin page:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from website.models import UserProfile
from django.contrib.auth.models import User
# Define an inline admin descriptor for UserProfile model
# which acts a bit like a singleton
class UserProfileInline(admin.StackedInline):
model = UserProfile
can_delete = False
verbose_name_plural = 'profile'
# Define a new User admin
class UserAdmin(UserAdmin):
inlines = (UserProfileInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Now, when I try to access a registered user via the admin menu, I get:
Caught DoesNotExist while rendering: User matching query does not exist.
In template /usr/local/lib/python2.7/dist-packages/django/contrib/admin/templates/admin/includes/fieldset.html, error at line 19
19 {{ field.field }}
And when I try to add a new user via the admin menu, I get:
Caught DoesNotExist while rendering: User matching query does not exist.
In template /usr/local/lib/python2.7/dist-packages/django/contrib/admin/templates/admin/includes/fieldset.html, error at line 19
19 {{ field.field }}
Why doesn't it recognise that particular field?
Edit: After looking on the full error message I can see the error is not solely related to extending User. The error happens when rendering checkboxes and corresponding labels that are used to assign prints to UserProfile you are editing/adding. Django admin is calling Print.__unicode__ for rendering label for each Print instance, which in turn access (on line 33 of /threedee/website/models.py) the Print's "printer" attribute which is a foreign key to User. And for some reason one of the Prints does have invalid printer value which doesn't point to any User.
Can't really tell what is really happening here without seeing the Print model, I recommend you checking the Print database table (should be named website_print) and find if there is anything unusual (are you using PostgreSQL?). If you are not having any important data there, truncating whole Print table should do the trick.
This is my old answer which you should still follow but it's not related to the error you are experiencing:
I would just comment on others answers but there doesn't seem to be a way of doing that for me. You need to combine both Alexey Sidorov's and Like it's answers:
First use django shell to create UserProfile instances for existing users - just run commads provided by Like it's answer in the shell (python manage.py shell).
After that you should setup signal that will automatically create UserProfile for each new user according to answer from alexey-sidorov.
Add this to models.py, after UserProfile class.
from django.db.models.signals import post_save
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
more info https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users
Add UserProfile entry for you existing users:
from django.contrib.auth.models import User
from website.models import UserProfile
for user in User.objects.all():
profile = UserProfile.objects.get_or_create(user = user)
and create profile for new user as well.
use signals for what.

Django Admin Complains About A Missing Field Even When It's Not Missing

Python 2.7
Django 1.3
When I include 'user_id','user' in the admin.py, then...
no user field shows up in the form when I click to add a Timeslip.
If I submit it anyway, then it shows the user field with a "This field is required." error message.
If I pick a user & submit again, then I get "'TimeslipAdmin.fields' refers to field 'user_id' that is missing from the form." even though 'user_id' is clearly listed in my admin.py (see below)
The Traceback says --
Exception Type: ImproperlyConfigured at /admin/timeslip/timeslip/add/
Exception Value: 'TimeslipAdmin.fields' refers to field 'user_id' that is missing from the form.
But...if I leave 'user_id','user' out of the admin.py then....
no user field shows up when I click to add a Timeslip.
Submit it anyway, and it shows the user field & a "Timeslip with this User already exists." error message. (which shouldn't be an error either 'cause I want users to have multiple Timeslip's which means another error I'll have to figure out once I can just get this form working)
admin.py
from timeslip.models import Timeslip
from django.contrib import admin
class TimeslipAdmin(admin.ModelAdmin):
fields = ['user_id','user','day','hours_as_sec','part_of_day','drove','gas_money','notes']
admin.site.register(Timeslip, TimeslipAdmin)
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Timeslip(models.Model):
user=models.ForeignKey(User)
day = models.DateField()
hours_as_sec = models.PositiveIntegerField()
part_of_day = models.CharField(max_length=16,choices=PART_O_DAY)
drove = models.BooleanField(default=False)
gas_money = models.DecimalField(max_digits=5,decimal_places=2)
notes = models.TextField()
class UserProfile(models.Model):
user = models.ForeignKey(User)
url = models.URLField("Website", blank=True)
position = models.CharField(max_length=50, blank=True)
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
I'm very clueless how to overcome this. I'm coming from a PHP background, a newbie to Python & Django.
user_id and user are redundant. Django automatically names your user field user_id in the database (since it holds the id for the User instance it points to.)
Change your admin.py to the following and it should work:
class TimeslipAdmin(admin.ModelAdmin):
fields = ['user','day','hours_as_sec','part_of_day','drove','gas_money','notes']
Also you're including all of the fields in the admin, so you really don't need to specify the fields. This would work just as well:
class TimeslipAdmin(admin.ModelAdmin):
pass
But...if I leave 'user_id','user' out of the admin.py then...
You haven't got 'user_id' field. So delete only this field and I think everything will work fine.

Reorder users in django auth

I have a model that has a ForeignKey to the built-in user model in django.contrib.auth and I'm frustrated by the fact the select box in the admin always sorts by the user's primary key.
I'd much rather have it sort by username alphabetically, and while it's my instinct not to want to fiddle with the innards of Django, I can't seem to find a simpler way to reorder the users.
The most straightforward way I can think of would be to dip into my Django install and add
ordering = ('username',)
to the Meta class of the User model.
Is there some kind of monkeypatching that I could do or any other less invasive way to modify the ordering of the User model?
Alternatively, can anyone thing of anything that could break by making this change?
There is a way using ModelAdmin objects to specify your own form. By specifying your own form, you have complete control over the form's composition and validation.
Say that the model which has an FK to User is Foo.
Your myapp/models.py might look like this:
from django.db import models
from django.contrib.auth.models import User
class Foo(models.Model):
user = models.ForeignKey(User)
some_val = models.IntegerField()
You would then create a myapp/admin.py file containing something like this:
from django.contrib.auth.models import User
from django import forms
from django.contrib import admin
class FooAdminForm(forms.ModelForm):
user = forms.ModelChoiceField(queryset=User.objects.order_by('username'))
class Meta:
model = Foo
class FooAdmin(admin.ModelAdmin):
form = FooAdminForm
admin.site.register(Foo, FooAdmin)
Once you've done this, the <select> dropdown will order the user objects according to username. No need to worry about to other fields on Foo... you only need to specify the overrides in your FooAdminForm class. Unfortunately, you'll need to provide this custom form definition for every model having an FK to User that you wish to present in the admin site.
Jarret's answer above should actually read:
from django.contrib.auth.models import User
from django.contrib import admin
from django import forms
from yourapp.models import Foo
class FooAdminForm(forms.ModelForm):
class Meta:
model = Foo
def __init__(self, *args, **kwds):
super(FooAdminForm, self).__init__(*args, **kwds)
self.fields['user'].queryset = User.objects.order_by(...)
class FooAdmin(admin.ModelAdmin):
# other stuff here
form = FooAdminForm
admin.site.register(Foo, FooAdmin)
so the queryset gets re-evaluated each time you create the form, as opposed to once, when the module containing the form is imported.