I am reading a book called 'Packt Publishing, Learning Website Development with Django' and I am doing a tutorial on how to create a bookmarking website where users can read articles and bookmark them / save the url to the article. Here is the models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
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)
Now, this is my user_page view (where the username is passed as a parameter to the view)
def user_page(request, username):
try:
user = User.objects.get(uesrname=username)
except:
raise Http404('Requested user not found.')
bookmarks = user.bookmark_set.all()
variables = {
'username': username,
'bookmarks': bookmarks
}
return render(request, 'user_page.html', variables)
I'm confused about the line
bookmarks = user.bookmark_set.all()
I understand that 'user' is a user object, but we are using the generic User model provided by Django, which only has username, password and email, right? So where is bookmark_set coming from? Because if it is trying to access all bookmarks from the bookmark class, shouldn't the 'b' in bookmark_set at least be upper case? and what does the _set do? In the book, it explains this line by saying
"To obtain the list of bookmarks for a particular user object, we can
conveniently use the bookmark_set attribute available in the user object"
I tried google'ing all the attributes available for the user object but couldn't find anything. Is there a website which has a list of all the attributes available for the user object?
The bookmark_set comes from the Bookmark definition and more specifically, from the line
user = models.ForeignKey(User)
This line creates a user attributes to the Bookmark model but also adds a bookamrk_set attribute to the User model!
This is explained in the django documentation. For completeness, I'm copying from 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().
bookmark_set is the reverse/"other side" of the ForeignKey. You have a FK from Bookmark to User, so Django automatically generates a bookmark_set on the User.
See also the official Django documentation: Following relationsips "backward"
You should have done the Django tutorial, where this is explained.
bookmark_set is the reverse relation for the ForeignKey that's defined in Bookmark and points to User. Every foreign key in Django gets this reverse accessor, which in this case gives you all bookmarks related to the particular User object you have.
Related
In the default Django Admin Site, if a model is registered with ForegingKey fields and those are included in readonly_fields (using the model property or the get_readonly_fields method) the value of the field is rendered with a link to the Change View, but this Doesn't work on a custom Django Admin Site.
E.g.: I have this two models:
class ModelA(models.Model):
field_a = models.CharField(max_length=50)
class ModelB(models.Model):
model_a = models.ForeignKey(ModelA, on_delete=models.PROTECT)
I registered in the default Django Admin Site:
admin.register(ModelA)
#register(ModelB)
class ModelBAdmin(admin.ModelAdmin):
model = ModelB
readonly_fields = ["model_a"]
So I get in the Change View of ModelB in the Admin the value of the field (str of the model) with a link to the Change View of the related model:
Link pointing to Chang View
But if I register the models in a Custom Admin Site, the link is not generated.
How can I extends my Custom Admin Site in order to generate those links?
PD1: I know I can code custom methods to build the links, but this is not a DRY way of do it and doesn't work well with get_readonly_fields method
PD2: If I register the related model (ModelA in the example) in the default Admin Site the link is genereted, but point to the default Admin Site, brokening the purpose of the Custom Admin Site.
I posted the same question in the official Django forum:
forum.djangoproject.com/t/9472/5
It has generated a ticket because is a missing line in the method get_admin_url in the helper class AdminReadOnlyField, The temporal solution is in the ticket: code.djangoproject.com/ticket/33077
PD: Why someone marks this post like a bad question? It has generates a ticket for a patch so it was a fair problem, this is my first question in StackOverflow and this is discouraging.
Question / Problem:
I am building a Django app, with 2 models: User and Secret. Secrets can be made by Users, and other Users can "like" them. I've setup my likes field as a ManyToManyField, so that Users whom like a Secret can be stored there and later retrieved, etc. However, when I try to query for a User and a Secret and use my_secret.likes.add(my_User) nothing happens. I don't receive an error and when I print my Secret's many-to-many likes field, after the add, I see: secrets.User.None.
Why is my add() method running but I am not receiving any errors, and why is my User not properly being added to my Secret's likes?
Note: I've saved both the User and Secret objects upon initial creation. Outside this application I've been able to use the add() method just fine, but in those scenarios I was creating objects in the moment, and not retreiving already existing objects.
Is there a different way to handle add() when using data retreived from a Query? That's my only other line of reasoning right now, and I've followed the documentation here exactly: Django Many-to-Many Docs
I also apologize if this was answered elsewhere on the site. I did find one other post here, but there was no solution provided, granted they were experiencing the exact same issue.
My Models:
class User(models.Model):
"""
Creates instances of a `User`.
Parameters:
-`models.Model` - Django's `models.Model` method allows us to create new models.
"""
first_name = models.CharField(max_length=50) # CharField is field type for characters
last_name = models.CharField(max_length=50)
email = models.CharField(max_length=50)
password = models.CharField(max_length=22)
created_at = models.DateTimeField(auto_now_add=True) # DateTimeField is field type for date and time
updated_at = models.DateTimeField(auto_now=True) # note the `auto_now=True` parameter
objects = UserManager() # Attaches `UserManager` methods to our `User.objects` object.
class Secret(models.Model):
"""
Creates instances of a `Secret`.
Parameters:
-`models.Model` - Django's `models.Model` method allows us to create new models.
"""
description = models.CharField(max_length=100) # CharField is field type for characters
user = models.ForeignKey(User, related_name="secrets") # One-to-Many Relationship
likes = models.ManyToManyField(User) # Many to Many Relationship
created_at = models.DateTimeField(auto_now_add=True) # DateTimeField is field type for date and time
updated_at = models.DateTimeField(auto_now=True) # note the `auto_now=True` parameter
objects = SecretManager() # Attaches `SecretManager` methods to our `Secret.objects` object.
Problem Example:
The model migrates fine, everything seems to be in proper syntax. However, when I try and retrieve a User and a Secret, and add the User to the Secret.likes, the add() method gives no errors, runs, but no objects are saved.
Here's an example:
tim = User.objects.get(email="tim#tim.com") # Gets a user object
my_secret = Secret.objects.get(id=2) # Gets a secret object
# This is where nothing seems to happen / take:
my_secret.likes.add(tim) # add() method per Django many-to-many docs
print my_secret.likes # returns: `secrets.User.None` -- why?
Why when printing my_secret.likes above, is nothing printed?
Especially when:
tim.secret_set.all() shows the secret containing an id=2 as in the above example....so the User is recording the relationship with the Secret, but the Secret is not recording any relationship with the User. What am I doing wrong?
You need to call the all method of the many-to-many field to view all related objects:
print my_secret.likes.all()
# ^^^^^
I have the following simple relationship:
class User(models.Model):
fields here
class UserProfile(models.Model):
user = models.OneToOneField(User)
I did the following in the shell:
user = User.objects.create(...)
profile = UserProfile.objects.create(user=user)
user.userprofile
...<UserProfile: UserProfile object>
user.userprofile.delete()
...(1, {'accounts.UserProfile': 1})
user.userprofile
...<UserProfile: UserProfile object>
From the above, you can see that I create User and UserProfile instances. Than I try to delete UserProfile instance and it is deleted (at least seems like). Than I do user.userprofile and it's like it never was deleted.
After little digging into the Django delete method, I realized that when I do user.userprofile.delete() Django just deletes userprofile's pk and the rest fields are not touched. What I do not understand is what should I do in order to get the following result:
user.userprofile.delete()
user.userprofile
...RelatedObjectDoesNotExist: User has no userprofile.
Does anyone have some ideas or code snippets?
You can reload the user from the database:
user = User.objects.get(pk=user.pk)
That will refresh all its attributes including the userprofile.
You can use Model.refresh_from_db:
user.refresh_from_db
Relevant docs
Due to my app requeriments I need an hierachy of users classes like:
class CommonUser(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
photo = models.ImageField(upload_to="profileImages",blank=True,null=True)
#HERE: Common properties for every user
class Meta:
abstract = True
class Installer(CommonUser):
#HERE: Specific properties for Installer Companies
class Administration(CommonUser):
#HERE: Specific properties for Administration
class Client(CommonUser):
#HERE: Specific properties for Clients
Well, in view.py I need to get the profile image for an user. I get the user by the request, so I have an models.User objects and I dont know to witch class it belong. Im not able to do:
request.user.commonuser.photo
because user object doesnt have any OnetoOne relation with commonuser but with installer/administration/client...
Any idea? Thanks!
I'm on mobile, so I can't test this, but...
I would set a "related_name" on your CommonUser.user field called "commonuser". Then you can just use:
request.user.commonuser.photo
like you are already.
I think the issue is that you are referencing a Django User object to reference a backwards relationship without the proper name.
First off, I think this model is more of a Profile than User. If you don't mind using 1.9 (and postgres) then this is a perfect usecase for a JSON field. You can filter with regular lookups and don't need to specify each type. That way you can also extend the user model in such a way that a user can fulfill many roles at once. The other thing I thought of was linking it the other way around:
class UserProfile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
# ...
class Installer(models.Model):
profile = models.ForeignKey(UserProfile, related_name='installer')
#HERE: Specific properties for Installer Companies
class Administration(models.Model):
profile = models.ForeignKey(UserProfile, related_name='admin')
#HERE: Specific properties for Administration
class Client(models.Model):
profile = models.ForeignKey(UserProfile, related_name='client')
#HERE: Specific properties for Clients
Have a UserProfile object that successfully extends the django default user class:
class UserProfile(models.Model):
user = models.OneToOneField(User)
and have updated the settings.py file accordingly:
AUTH_PROFILE_MODULE = 'authorization.UserProfile'
Everything works fine, just wondering: how do I get to objects associated with UserProfile for a given context within a view?
Assume that I can just get context.user if the user is logged in, but then how do I grab the corresponding UserProfile object?
In view:
request.user.get_profile().field_name
In template:
{{user.userprofile.field_name}}
You can get it like -
context.user.userprofile
Detail here.