Many to many relation django in post to signs to post - python

So I want to make a sign-ups model for my app. This model needs to have 2 fields, a post field and a users field. Both fields are a many 2 many relation with the User model and Post model. But I don't know how to achieve that.
Here is what I have:
this is the models.py
class Post(models.Model):
title = CharField(max_length=250)
...
def __str__(self):
return self.title
class SignUpPost(models.Model):
post = models.OneToOneField(Post,on_delete=models.CASCADE,primary_key=True,)
users = models.ManyToManyField(User)
so many users can be signed up in a post. I have no idea how to do the forms.py and the views.py to store the data.
I want to have in the post detail view a "sign up" button, and when clicked, fill the sign up form with the data of the post and user that clicked the button. And so, the database will have in the post field the post_id and in the users field the users_id of every user that signed up to the post.
thanks in advance

Related

Django Model: ForeignKey and Relations

I have 2 models:
class Post(models.Model):
pass
class Vote(models.Model):
post = models.ForeignKey(Post)
user = models.ForeignKey(django.contrib.auth.models.User)
I want to allow logged User to make a vote on Post's Admin site. I think about 2 solution as below:
Add 'voted' field to Post model.
Customize Post's Admin forms by add a Button control and implement do_vote() function in Post model to call when the Button is clicked.
Do you have any other solutions?
Additionally, I don't know how to implement 2 above solutions.
Could you give me some lines of code?
On your PostAdmin class you can add an action:
class PostAdmin(admin.ModelAdmin):
...
actions = [vote_on_post,]
and then you can implement the vote_on_post method based on this documentation, should be something like this:
#admin.action(description='Vote on action')
def vote_on_post(modeladmin, request, queryset):
user = request.user
for post in queryset:
Vote.objects.create(user=user,post=post)
You probably want to add logic to prevent multiple votes from the same user on the same post, etc.

restricting user to like the post once in Views.py not in model

I know I can do this easily in models.py but I don't have like model in my app. I have post app and there's like field. so I want to restrict it in my views.py
Here's my try but I don't understand why it won't restrict it
#login_required
def like_post(request, pk):
if User.objects.filter(pk = pk, id = request.user.id).exists():
return HttpResponse('You already voted for this, cheater')
else:
liked_post = Post.objects.get(id=pk)
count = liked_post.likes
count += 1
liked_post.likes = count
liked_post.save()
return redirect('/community/post/%s' %liked_post.id)
First of all about mistakes:
In "if" statement you are trying to find a user by a post primary key. Moreover, you are performing a search both by id and pk, while they are technically the same in your case. See: Django queries - id vs pk
As for implementation:
I am afraid it's impossible to track "cheaters" in your implementation. You cannot understand that "This user already liked this post" without storing likes. There is no data that tells you this fact. "Else" part is ok, but if you want to track "likers", some customisation is needed.
The easiest option is to add ManyToMany field referencing User to your Post model and call it "likers". The table with columns "user_id", "post_id" will be created under the hood and will tell that "This user liked this post".
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
...
likers = models.ManyToManyField(User)
...
And in your view liked_post.likers.add(request.user), to add current user to likers and liked_post.likers.filter(user=request.user).exists() to find "cheaters"

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

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())

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.

Save user specific data in Django

I am hacking Django, as I am new to it, to create a website to which user can login and can answer some questions. For logged in user I intend to store their username, question id and response to the question. However, after trying for multiple hours I have been completely unsuccessful. Below I have given snippets of Models.py and Views.py
Models.py - I am copying only UserProfile class and Userresponse class which are needed to create the User Profile and User Response table
# Model class for creating user
class UserProfile(models.Model):
user = models.OneToOneField(User)
def __str__(self):
return self.user.username
# Model class for getting user response
class UserResponse1(models.Model):
user = models.ForeignKey(UserProfile, default=0)
questoinId = models.ForeignKey(Question)
option = models.IntegerField(default=0)
```Views.py``
def response(request, question_id):
q = UserResponse1()
if request.user.is_authenticated():
q.user = request.user.username
q.questionId_id = question_id
q.option +=request.POST['choice']
q.save()
# Redisplay the question voting form.
return HttpResponseRedirect(reverse('polls:overallResults'))
However, on running above I get following error - Cannot assign "u'abc123'": "UserResponse1.user" must be a "UserProfile" instance.
abc123 is the login name of the user. I am not able to figure out the reason for this error. Any help on fixing this error so that I can write the data to UserResponse1 table, will be very helpful.
I am using Django 1.8 on Python 2.7
q.user is a foreign key to the UserProfile table, so you have to assign a user profile instance.
Since you have access to the user with request.user you can access the user profile using the one to one field.
user_profile = request.user.userprofile
q.user = user_profile

Categories