Foreign key to User table in django - python

I'm using django's built-in contrib.auth module and have setup a foreign key relationship to a User for when a 'post' is added:
class Post(models.Model):
owner = models.ForeignKey('User')
# ... etc.
Now when it comes to actually adding the Post, I'm not sure what to supply in the owner field before calling save(). I expected something like an id in user's session but I noticed User does not have a user_id or id attribute. What data is it that I should be pulling from the user's authenticated session to populate the owner field with? I've tried to see what's going on in the database table but not too clued up on the sqlite setup yet.
Thanks for any help...

You want to provide a "User" object. I.e. the same kind of thing you'd get from User.objects.get(pk=13).
If you're using the authentication components of Django, the user is also attached to the request object, and you can use it directly from within your view code:
request.user
If the user isn't authenticated, then Django will return an instance of django.contrib.auth.models.AnonymousUser. (per http://docs.djangoproject.com/en/dev/ref/request-response/#attributes)

Requirements --> Django 3, python 3
1) For add username to owner = models.ForeignKey('User') for save that, in the first step you must add from django.conf import settings above models.py and edit owner = models.ForeignKey('User') to this sample:
class Post(models.Model):
slug = models.SlugField(max_length=100, unique=True, null=True, allow_unicode=True)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, default=1, on_delete=models.CASCADE)
2) And for show detail Post, special owner name or family or username under the post, you must add the following code in the second step in views.py:
from django.shortcuts import get_object_or_404
.
.
.
def detailPost(request,slug=None):
instance = get_object_or_404(Post, slug=slug)
context = {
'instance': instance,
}
return render(request, template_name='detail_post.html', context=context)
3) And in the third step, you must add the following code for show user information like user full name that creates a post:
<p class="font-small grey-text">Auther: {{ instance.owner.get_full_name }} </p>
now if you want to use user name, you can use {{ instance.owner.get_username }}
or if you want to access short name, you can use {{ instance.owner.get_short_name }}.
See this link for more information.

Related

How to give data to ManyToManyField from view in django

Hello i am new to django and i am trying to create a page where we can add and participate in various events.
This is the model i created for my database
model.py
class Venue(models.Model):
name = models.CharField('Venue Name', max_length=120)
address = models.CharField(max_length=300)
zip_code = models.CharField('Zip Code', max_length=6)
phone = models.CharField('Contact Number', max_length=25, blank=True)
web = models.URLField('Website Address', blank=True)
email = models.EmailField('Email', blank=True)
owner = models.IntegerField('Venue Owner', blank=False, default=1)
venue_image = models.ImageField(null=True, blank=True, upload_to="images/")
class Event(models.Model):
name = models.CharField('Event Name', max_length=120)
event_date = models.DateTimeField('Event Date')
venue = models.ForeignKey(Venue, blank=True,null=True, on_delete=models.CASCADE)
manager = models.ForeignKey(User,blank=True,null=True,on_delete=models.SET_NULL)
description = models.TextField(blank=True, )
attendees = models.ManyToManyField(User,blank=True, related_name='attendees')
here i am trying to make a link by clicking that link user participate to that Event
but i am not getting how to put the user data in the above attendees field
view function
def attendees(request):
Event.attendees.add(request.user)
return redirect('list-events')
error : AttributeError at /participate/
'ManyToManyDescriptor' object has no attribute 'add'
link
Participate
url.py
path('participate/', attendees, name='participate')
You need to specify for which event the user will be added, so the view should look like:
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect
from django.views.decorators.http import require_POST
#require_POST
#login_required
def attendees(request, pk):
event = get_object_or_404(Event, pk=pk)
event.attendees.add(request.user)
return redirect('list-events')
participating should be done through a POST request since it alters entities. A GET request should only be used to retrieve data, not update it.
and the urls.py thus should contain a URL parameter for the primary key:
path('participate/<int:pk>/', attendees, name='participate')
Finally the template should thus make a POST request to the path with the primary key of the event:
<form method="post" action="{% url 'participate' pk=event.pk %}">
<button type="submit">Participate</button>
<form>
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
Note: One can use the #require_POST decorator [Django-doc]
to restrict the view to only be accessible for a POST request.
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.
Here is the easiest way of doing it. As I can understand Venue model is saved by the admin(i.e you). If not then you have to save the Venue first.
Now the key point is, in relationship fields, you have to pass the whole inheriting model object.
You can do something like this:
#in login view
def login(request):
#your code
request.session['userID']=userID
#your code
now you can use the session variable (i.e userID) in any view or template
#participate view
from mymodels import Event,Venue
def participate(request):
#your GET or POST parameters here
user=user_model.objects.get(id=request.session['userID'])
venue=Venue.objects.get(id=venue_id) #OR filter it by any other field like name
E1=Event()
E1.attendees= user
E1.venue= venue
E1.manager= user
E1.name= name_from_request_parameters
E1.description= description_from_request_parameters
E1.event_date= event_date_from_request_parameters
E1.save()
Tip: use default current date in datefield like
event_date = models.DateTimeField(default=datetime.now())

Django: How to retrieve the logged in user's details

I am in the process of learning Django. I am trying to create a simple directory web app. I am able to print out all the users details for the main directory page. However, I want to add a feature that when a person logs into the directory app they are brought to their 'profile' page where they will be able to see all their own details e.g. business name, profile pic.
I know how to retrieve the default fields e.g. username and email. But cannot seem to retrieve the custom fields that I declared myself. Here is my attempts so far...
Models.py:
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class UserProfileInfo(models.Model):
user = models.OneToOneField(User,on_delete=models.DO_NOTHING)
#additional classes
business_name = models.CharField(max_length=191,blank=True)
trade = models.CharField(max_length=191,blank=True)
portfolio_site = models.URLField(blank=True)
profile_pic = models.ImageField(upload_to='profile_pics',blank=True)
def __str__(self):
return self.user.username
Views.py:
#login_required
def profile(request):
profile = UserProfileInfo.objects.filter(user=request.user)
context = { 'profile': profile }
return render(request, 'dir_app/profile.html',context)
Profile.html:
<div class="container-fluid text-center">
{% for p in profile %}
<h3>{{p.business_name}}</h3>
{% endfor %}
</div>
Since UserProfileInfo is related to User via OneToOneField, you can have one UserProfileInfo per User. So, instead of Filter, you can simply get your desired UserProfileInfo object through your current (logged in) User as follows.
views.py,
profile = UserProfileInfo.objects.get(user=request.user)
Also, before you can get a request.user object, you have to make sure that your user is authenticated and logged in. Otherwise, you might get None in place of a User object and therefore, no associated UserProfileInfo.
Since it is a OneToOneField there is only one Profile object for a User, you thus can obtain this with:
#login_required
def profile(request):
profile = request.user.userprofileinfo
return render(request, 'my_template.html',{'profile': profile})
Then in the template, you render it with:
{{ profile.business_name }}
you can use it directly on template without sending it f:
{{request.user.userprofile}}

get person id through authenticated user id

I try to get person id of my Person model, connected with User model
from django.db import models
from django.contrib.auth.models import User
class Person(models.Model):
user = models.ForeignKey(User, blank=False, null=False, on_delete=models.CASCADE)
middle_name = models.CharField(max_length=30)
def __str__(self):
return '%s %s %s' % (self.user.last_name, self.user.first_name, self.middle_name)
from template-generated page, but I only can get user id who authenticated right now
{{ user.id }}
but not Person id.
How can I get Person id through user id on the page?
django automatic reverse lookup foreign key (user field) from an model, if you want to get person id try this {{user.person.id}}
I suggest OneToOneField for this kind of relation.
user = models.OneToOneField(User, blank=False, null=False, on_delete=models.CASCADE)
then you can use user.person
I think you are having logical issue with your model. One person generally should have connected with one user so you should user OneToOneField instead of ForeignKey. Let's think you are correct then you can follow the code below, if you are not then it will work too.
You can get it through context variable
Suppose your person=Person.obejcts.get(id=1)
Then pass it through context in view like
def view(request):
person=Person.obejcts.get(id=1)
context = { 'person':person}
return render(request, 'your.html', context}
And in template you can use {{person.id}}
You can simply do {{ user.person_set.get(pk=1) }} . This assumes you know the pk...if you only actually store 1 person with 1 user, then substitute the .get() with a .first() ...you can access your foreign key model using the main_obj.fk_obj_set.all() and do things just like any other queryset. This obviously assumes you have multiple users for 1 person..otherwise things get easier with a 1:1 field.

django 2 - User and userprofile models, how to obtain all fields in a single query?

Update and solution below.
I've been looking for a solution but I'm not finding anything that sticks out.
I've created a Profile model which is linked to the standard User model via one-to-one field which is working in admin. I want to pull all fields/data for both models into a single queryset. I'm trying to create a user editing form and I want to pull in all fields for User and Profile based on the current logged in user and display those fields which I will have a page to edit and save those fields.
What are the best options to achieve this, simple is better.
class Profile(models.Model):
address = models.blablabla
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
def profile_edit(request):
form = UserProfileForm(request.POST or None)
instance = Profile.objects.all().filter(user__username=request.user).values # This is the place I need to create a single queryset.
if request.method == "POST" and form.is_valid():
form = UserProfileForm(request.POST, instance=instance)
user_form = form.save()
print("POST event")
else:
form = UserProfileForm(instance=instance)
print(form)
return render(request, 'frontend/profile_edit.html', {'form': form})
I'm manually creating the forms in the template so I would like to have something like {{ form.username }} {{ form.profile.address }} or something like that. I'm likely doing things poorly, I'm new to django.
UPDATE
Complete solution
Complete steps to gain access to user and profile models in code and template.
I decided not to replace the User model with my own in case I missed out on features provided by django. It also seemed to complicate things that might hurt later on. So I've gone with the separate UserProfile model and attached it to the User model. Here is what I did for future readers.
models.py
from django.db.models.signals import post_save
class UserProfile(models.Model):
#take note of the related_name='profile' this is used to reference the fields in code and template.
#Each field of type 'text' I added default='' at the end, I got an error when it was int based so I removed the flag for that field. I read this might cause an error when you try and auto-create the profile, see what works for you and you might not want it.
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
country = models.CharField(max_length=2, blank=True, null=True, default='')
...
# Auto-create the profile upon user account creation. It's important to start with a fresh database so the user and profile ID's match.
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
# Create your models here.
#In code, you can access your user and profile field data like so.
request.user.profile.fieldname
request.user.fieldname
In template you can do the same
{{ user.fieldname }}
{{ user.profile.fieldname }}
There's no need for a query here at all - you don't want a queryset, you want a single instance.
In this case, request.user.profile will give you the entire Profile object related to the current user.

Getting current logged_in user in Django

I'm developping a blog with Django, and I'd like my posts to be related to their author. I've created an author field in my Blog model class, which is a ForeignKey pointing on a User object. Problem is, South database migration django tool asks me for a default value to give to it. Actually, I'd like it to be the id of the logged in user who edited the post, but can't see how to make it. Any idea?
This is my Blog model class:
class Blog(models.Model):
PUBLISHING_STATUS = (
('P', 'Published'),
('D', 'Draft'),
('AA', 'Awaiting Approval'),
)
title = models.CharField(max_length=128, unique=True)
slug = models.SlugField(max_length=160, unique=True)
header_image = models.ImageField(upload_to='images/uploaded', max_length=256, null=True)
body = models.TextField()
author = models.ForeignKey(User)
status = models.CharField(max_length=2, choices=PUBLISHING_STATUS, null=True)
posted = models.DateTimeField('date posted')
category = models.ForeignKey('blog.Category')
tags = models.ManyToManyField('blog.Tags')
def __unicode__(self):
return self.title
def get_absolute_url(self):
return '/blog/%s/view_post' % self.slug
Thanks!
If you want the field to be required you will have to set a default value when migrating. For the reason you don't have stored any data anywhere about who created the post, you ca only set it to a specific user, which in this case probably should be your administrator user!
You could then edit the posts in the admin and set them to the correct author, if necessary!
If you are looking for a way to auto-populate the field upon saving a new post in the admin, you have to override the save_model method on your ModelAdmin:
class BlogPostAdmin(model_admin):
def save_model(self, request, instance, form, change):
user = request.user
instance = form.save(commit=False)
if not change or not instance.author:
instance.author = user
instance.save()
form.save_m2m()
return instance
Problem is, South database migration django tool asks me for a default value to give to it.
Why? Because you have old data that you're trying to preserve? Perhaps you shouldn't be trying to keep any old data.
Actually, I'd like it to be the id of the logged in user who edited the post, but can't see how to make it. Any idea?
That's what view functions are for. You don't do this in the model. You do this in the view function that handles the Form.
You should do this as a two step process. First, do a schema migration with any default value to create the new structure. Then, do a data migration to update the author column with the correct value from the user table.

Categories