showing which post a user liked/saved with python django. - python

I have trouble to display the ''saved''/''liked'' posts of my users in django/admin. I would like to have a field in the Adminpage to show which user likes which posts. I made an Userprofile model where all extra information (besides the one on the given django admin user Profile) are stored. so here is my model View:
class UserProfile(models.Model):
user = models.OneToOneField(User, null=True)
#likes = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True,default=1, related_name='likes')
likedPosts=models.ManyToManyField('self')
Field1 = models.CharField(max_length=50,default='Sunny')
Field2 = models.CharField(max_length=50,default='')
class Meta:
ordering =['-user']
#def __unicode__(self):
# return self.user.username
User.profile =property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
right now in the liked post field I have only some usernames or "User object"
I tried all kinds of combinations to get the information into the admin page but as you can see I did not make it.
I tried to change the unicode and of course the liked post line. If you need more information please tell me so. I appreciate every kind of help.

django admin isn't really meant to support many to many relationships from both directions in the django admin. However, the link below contains a workaround that I think should address your problem with a better explanation of why many-to-many relationships are only shown from one side by default.
(many-to-many in list display django).

so for everybody who wants to do something similar this worked for me:
class UserProfile(models.Model):
likedPosts = models.ManyToManyField('self',default=None,blank=True)
def __unicode__(self):
return "{0}".format(self.user.likes.all())

Related

Differentiate between 'account types' in Django

Thanks in advance if you're reading this... I'm a High School student working on a web application using Django, to help students find internships, and facilitate parents posting internship offers -- a sort of marketplace if you will.
I'm trying to create a profile/account page for the users but I need a way to differentiate between whether the account logged in is a Student or Employer so that I can use views.py to generate a page appropriate to their account.
In models.py, I have two different profile types which can be associated with a user account (handled by django.contrib.auth), see below for reference.
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profilePic = models.ImageField(default='default.jpg', upload_to='profile_pics')
class Meta:
verbose_name = 'Student Profile'
def __str__(self):
return f"{self.user.username}'s Profile"
class Employer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profilePic = models.ImageField(default='default.jpg', upload_to='profile_pics')
company = models.CharField(max_length=100, default='Unspecified')
class Meta:
verbose_name = 'Employer/Parent Profile'
def __str__(self):
return f"{self.user.username}'s Profile"
In my views.py page, I'm trying to create a view for the account/profile that can detect whether the currently logged-in user's profile is linked to either the 'Student' or 'Parent' model and serve a page accordingly. I've tried a very rudimentary approach, as below, but unsurprisingly it's not working.
def account(request):
if user.student.username == True:
context = 'Account: Student'
return render(request, 'users/studentprofile.html', context)
elif user.employer.username == True:
context = 'Account: Employer'
return render(request, 'users/employer.html', context)
I was wondering if anyone had a suggestion as to how I can best accomplish this... apologies in advance is this approach is poorly structured or against the status-quo of Django Programming, I'm a complete beginner!
Thanks in advance all :)
As Morteza Afshari said, you should reorganize your models to include a boolean field.
class CustomUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profilePic = models.ImageField(default='default.jpg', upload_to='profile_pics')
is_student = models.BooleanField(default=True)
class Meta:
verbose_name = 'Profile'
def __str__(self):
return f"{self.user.username}'s Profile"
This follows the DRY principle much better than your prior code because now we're not repeating fields like user, profilePic, etc.
Now we can rewrite your views.py like this:
def account(request):
if user.is_student:
context = 'Account: Student'
return render(request, 'users/studentprofile.html', context)
else:
context = 'Account: Employer'
return render(request, 'users/employer.html', context)
it would be better if you posted your exception/error alongside your code
but here's some hint:
context parameter passed to render function should be a dictionary not an string
context = {'Account: Student'}
and you should access to user with request.user not just user
if problems above didn't solve your problem
add these two lines of code at the beginning of your function:
print(request.user.student)
print(request.user.employer)
You can have an boolean field in user model like is_student and fill it during sign in. It can be null=True and null when user signed out.
If you have jwt token, you can store additional data in token to check where it comes from, or either store user current role in its cookie. Get us more data about your site structure for more related answers. (Data about authentication system, database structure or any more structural behaviors)

Overriding admin form for a model in django

Here's my models.py
from multiselectfield import MultiSelectField
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
users = User.objects.values_list('id','username')
authorized = MultiSelectField(choices=users, null=True)
def __str__(self):
return self.question_text
My problem is that while server is running after user register my choices field are not updating till rerun my server.
I did some research and i found solution for that:
https://mschmitt.org/blog/dynamic-django-form-choice-labels/
http://www.ilian.io/django-forms-choicefield-with-dynamic-values/
I have no idea how to override model in forms + i need to override model in django admin forms. any pointers would be great!
thanks in advance
You should not do this at all. If you want to store related objects, you should use a proper database relationship.
In this case, a ManyToManyField to User would be appropriate, since a question can have multiple authorized users and presumably a user can be authorized for multiple questions.

Getting logged in User's Profile in Django without repeating request.user a lot?

The application I am writing in Django requires me to get a UserProfile model object (which has a one-to-one relationship with the standard Django User object) in a lot of the views. To get the user's profile, I end up repeating something like this quite a bit in all the different views:
user_profile = UserProfile.objects.get(user=request.user)
or
user_profile = UserProfile.objects.get(user=self.request.user)
I know good software engineering principles say don't repeat yourself (DRY), so I was wondering if there was a good way to encapsulate the code above in a separate method or if it's fine to keep it the way it is.
Thanks in advance for any help!
Add a related name to your UserProfile in models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
about = models.TextField(default='')
Then in your views.py, reference it with
request.user.profile.about = 'abc'
request.user.profile.save()
Or make it shorter
p = request.user.profile
p.about = 'abc'
p.save()

Django reverse url to onetoonefield on success

Good day SO!
Recently I've started working on Django, got myself a situation which I can't find the right solution on the web to solve it. So I've got a little question about URL reversing on a success. Currently when an user successfully creates an account, the user gets reversed to a profile based on the user_id which just got created:
class Create(CreateView):
form_class = UserCreateForm # inherits from django's UserCreationForm
def get_success_url(self):
return reverse('users:profile', kwargs={'pk': self.object.pk})
This is working properly. Now I created a profile module with a OneToOneField
to the django.auth.models User model. When creating a new account, a signal is send to the create_profile_on_registration method.
#receiver(post_save, sender=User)
def create_profile_on_registration(sender, created, instance, **kwargs):
...
This is also working properly, a new profile is created on user account registration. Now I would like to reverse the user to the new created profile_id instead of the user_id. However, I cant figure out exactly how to get this properly to work. Can you give me a little push in the right direction on how to approach this issue? I can't match up the right google search words or find any examples which explains or shows how to achieve this properly.
Thanks in advance!
When you create a one to one field to your user model,
class Profile(models.Model):
user = models.OneToOneField(User)
you can access the user from the profile
profile.user
and you can also access the profile from the user
user.profile
In your view, self.object is the user, so self.object.profile.id will give you the profile id.

Django NOT NULL constraint failed userprofile.user_id in case of uploading a file

I am trying to practice a simple project: A user registers (using Django registration-redux), uploads some file, and then s/he is provided with a list of her files, being downloadable. Here are my models.py, forms.py, and views respectively:
models.py
class UserProfile(models.Model):
user = models.ForeignKey(User, related_name='uploaded_by')
names = models.CharField(max_length=40)
lastname = models.CharField(max_length=50)
email = models.EmailField()
uploads = models.FileField(upload_to= 'blablabla')
def __str__(self):
return self.email
forms.py
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['names', 'uploads']
view.py
from .forms import UserProfileForm
from .models import UserProfile
#login_required()
def new(request):
form = UserProfileForm(request.POST or None, request.FILES or None)
if form.is_valid():
form.save()
context = {'title': 'welcome', 'form': form}
return render(requst, 'upform.html', context)
however when I login with a user and try to upload a file I get the error: IntegrityError at /new
NOT NULL constraint failed: _userprofile.user_id
After digging a lot I noticed someone suggested the reason of the error is because the user is not included anyhow in the process of posting the form, so I tried whatever came to my mind and the case in which I added the user field to the forms.py worked:
forms.py
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['names', 'uploads']
the problem however is that the form shown in the browser now includes a drop-down list containing all the registered users. I tried to associate the logged-in user with the form in the views by I kept seeing different errors.
My question is: How can I associate the uploads with the logged-in user in a transparent manner.
sorry if the question is too newbie-liked
Keep the user out of the form and add it on save:
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.save()
I must say your model looks a bit odd; you have multiple profiles for each user, each with a single upload. Seems more likely you want a single profile, with a OneToOne relationship to User, than a separate Uploads model with a ForeignKey to UserProfile.
I added the user field to the forms.py worked:
This probably also opens up a security hole, because you could then set the user from outside of your application, overriding the logged-in user.
After digging a lot I noticed someone suggested the reason for the error is because the user is not included anyhow in the process of posting the form.
You figured that quite right. If the user is not a field, the form never knows how to fill the user field of UserProfile. Since you cannot save a UserProfile without a filled in user foreign key, you get the error.
There are various ways around this:
One way to solve the problem is to use save(commit=False) on the form, patch the user into the unsaved instance and manually saving the object to the database:
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.save()
This slightly violates the encapsulation because you now handle the database save outside of the form.
You are also able to provide a initial "template" instance to the form:
form = UserProfileForm(
request.POST,
request.FILES,
instance=UserProfile(user=self.request.user)
)
You probably want to do so, anyway because the form also allows to edit an existing userprofile. Currently you are saving a new UserProfile each time and since user is not unique=True in your model class, you will get multiple profiles per user.
If you do not want this to be possible, check Daniel Roseman's answer, because then you probably want UserProfile.user to be a OneToOne field.
In this case, we can simplify to
profile, created = UserProfile.objects.get_or_create(user=request.user)
form = UserProfileForm(
request.POST,
request.FILES,
instance=profile
)
Note also that I removed the or None as it is not necessary. BaseForm (which ModelForm derives from) does this check for you (it actually does self.data = data or {}) which essentially undoes the prior none-ification)
You can do following the advice of Daniel.
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='uploaded_by')
names = models.CharField(max_length=40)
lastname = models.CharField(max_length=50)
email = models.EmailField()
class UserFiles(models.Model):
user = models.ForeignKey(UserProfile)
file = models.FileField(upload_to= 'blablabla')
Remember OneToOneField is similar a ForeignKey with the attribute unique=True.

Categories