Django using add to add a ManyToManyField where the object is 'self' - python

When I execute the following code profile is added to user_profile.following as was expected, but user_profile is also added to profile.following (this is unwanted) why is this happening, I have a feeling it has something to do with the ForeignKey being 'self', but I'm not sure how to fix it...Here is the view:
def follow(request, profile_id):
user = request.user
profile = get_object_or_404(Profile, pk=profile_id)
user_profile = get_object_or_404(Profile, pk=user.id)
user_profile.following.add(profile)
return HttpResponseRedirect(reverse('twitter:profile', args=(profile.id,)))
and the model:
class Profile(models.Model):
user = models.ForeignKey(User)
bio = models.TextField()
image = models.ImageField(upload_to='images/%Y/%m/%d/')
following = models.ManyToManyField('self')

Well I finally found the answer to this after some more snooping, in case anyone else wants to know the solution: in the model, following should be
following = models.ManyToManyField('self', symmetrical=False)

Related

NOT NULL constraint failed: users_userprofile.user_id

I am trying to insert django form data inside the UserProfile model in my app. I tried using the django shell and views.py but I keep getting this error.
Models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
section = models.CharField(max_length=255, blank=True)
year = models.IntegerField(null=True, blank=True)
course = models.CharField(max_length=255, blank=True)
qrcode = models.CharField(max_length=255, blank=True)
present = models.BooleanField(default=False)
def __str__(self):
return self.user.username
views.py
#staticmethod
def generate_qr(request):
if request.method == "POST":
form = MakeAttendance(request.POST)
if form.is_valid():
course = form.cleaned_data.get('courses')
section = form.cleaned_data.get('section')
year = form.cleaned_data.get('year')
profile = UserProfile.objects.get_or_create(user=request.user)
userobj = UserProfile(qrcode=unique_id)
userobj.save().filter(course=course, section=section, year=year)
return redirect('/users/dashboard')
This question has been answered many times here, but none of the solutions worked for me. I tried Creating a user profile with get_or_create method. I tried deleting my entire database and making migrations again. I manually tried to pass the user ID but nothing.
First create a user using user=User.objects.create_user(username=request.user, password='password'), then save it using user.save() and create profile using profile=UserProfile.objects.get_or_create(user=user). The reason this error occours is because the UserProfile looks for a user instance which you did not provide.
The problem is in these two line
userobj = UserProfile(qrcode=unique_id)
userobj.save().filter(course=course, section=section, year=year)
In the first line you created an instance of UserProfile with only qr_code
and in the next line you are trying to save it which will try to insert a new row in the database without the user.
in models.py you should create user object:
from django.conf import settings
User = settings.AUTH_USER_MODEL
before class creating

User Follower model on Django. Cannot use add() on a ManyToManyField which specifies an intermediary model. Use accounts.Contact's Manager instead

I am new to Django, Please forgive any silly mistakes in code or logic,
Intro: I am trying to create a user follower model in Django. Where users can follow and unfollow other users on the sites
Error: I have made the models for my follow/unfollow I have also made the views I am getting this error
AttributeError at /accounts/admin/follow/
Cannot use add() on a ManyToManyField which specifies an intermediary model. Use accounts.Contact's Manager instead.
The obj.followers.add(user) is highlighted in the traceback as the origin of the error
Below are my models.py
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
city = models.CharField(max_length=100)
country = models.CharField(max_length=100)
def get_absolute_url(self):
return reverse('accounts:profile', kwargs={'username': self.user.username})
class Contact(models.Model):
user_from = models.ForeignKey(User, related_name='suppporter')
user_to = models.ForeignKey(User, related_name='leader')
def __str__(self):
return '{} follows {}'.format(self.user_from, self.user_to)
User.add_to_class('following',
models.ManyToManyField('self', through=Contact, related_name='followers', symmetrical=False))
I think the models.py may be good. The fault I believe is in my views.
Below is my view.py
class FollowToggle(LoginRequiredMixin, RedirectView):
def get_redirect_url(self, *args, **kwargs):
username = self.kwargs.get('username')
print(username + " This is the user who will be followed") # This prints correct
profile = get_object_or_404(Profile, user__username=username)
print(profile) # This prints correct
obj = get_object_or_404(User, username=username)
print(obj) # This prints correct
url_ = profile.get_absolute_url()
print(url_) # This prints correct
user = self.request.user
print(user) # This prints correct
if user.is_authenticated():
if user in obj.followers.all(): # I know this is the source of the error.
obj.followers.remove(user)
else:
obj.followers.add(user)
return url_
Below are the Urls.py just in case
url(r'^(?P<username>[-\w]+)/follow/$', views.FollowToggle.as_view(), name='follow'),
You cannot use add and remove method for manytomany relation defined through third model. From the docs:
Unlike normal many-to-many fields, you can’t use add(), create(), or set() to create relationships
Instead you should use Contact manager:
if user.is_authenticated():
if user in obj.followers.all(): # I know this is the source of the error.
Contact.objects.filter(user_to=obj, user_from=user).delete()
else:
Contact.objects.create(user_to=obj, user_from=user)
In Django 2.2 you can use add, remove and set methods (Docs)
You can also use add(), create(), or set() to create relationships, as long as your specify through_defaults for any required fields

Filtering Django User Choice Field use for a form

I am working on a hard django project and I am stuck again. I have a field in the userprofile which is called troop:
class UserProfile(models.Model):
scout_username = models.OneToOneField(User, on_delete=models.CASCADE)
Group_Choice = Groups.Scout_Groups()
troop = models.SlugField(max_length=27, choices=Group_Choice, default='None', blank=False)
date_of_birth = models.DateField(default=date.today)
def __str__(self):
return '%s'% (self.scout_username)
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
Then I have a form which fills in data which is sent to my stData model. Within the form the user can choose to add details about another user. Except they can only add details to another user who has the same troop details.
forms.py
from django import forms
from leaders.models import stData
from django.contrib.auth.models import User
from accounts.models import UserProfileManager, UserProfile
st_username_list=[
(None, 'Choose a user'),
(user1, 'user1'),
(i, 'no progress'),
]
class BadgeForm(forms.ModelForm):
def set_user(self, user):
global st_username_list
troop = user.userprofile.troop
userprofile = UserProfile.objects.all()
selected_st = userprofile.filter(troop=troop)
for st in selected_st:
username = str(st.st_username)
st_username_list.append((username, username))
st_username = forms.ChoiceField(choices=st_username_list)
class Meta:
model = stData
fields = ('st_username', 'Pioneer_Badge', 'Explorer_Badge', 'Adventurer_Badge', 'Proficiency_Badge', 'Other_Badge')
Please note
In the example above I used a global variable. I understand this is far from desired. I have since removed it thanks to the explanation of the proper way to do the filter (found after the line break). I'm only keeping this for education reasons for others who may find they had similar problems.
I pass through the user within my views like this:
user = request.user
user_form_setting = BadgeForm.set_user(self, user)
models.py
from django.db import models
from django.contrib.auth.models import User
from accounts.st_badge_list import st_List
class stData(models.Model):
Pioneer_Choices = st_List.Target_Badges()
Blue_Choices = st_List.Target_Badges()
Black_Choices = st_List.Target_Badges()
Proficiency_Choices = st_List.Proficiency_Badges()
Other_Choice = st_List.Other_Badges()
Pioneer_Badge = models.CharField(max_length=16, choices=Pioneer_Choices, default='None', blank=True)
Blue_Star = models.CharField(max_length=16, choices=Blue_Choices, default='None', blank=True)
Black_Star = models.CharField(max_length=16, choices=Black_Choices, default='None', blank=True)
Proficiency_Badge = models.CharField(max_length=22, choices=Proficiency_Choices, default='None', blank=True)
Other_Badge = models.CharField(max_length=27, choices=Other_Choice, default='None', blank=True)
st_username = models.ForeignKey(User, on_delete=models.CASCADE)
date = models.DateTimeField(auto_now=True)
print (User)
def __str__(self):
return '%s'% (self.st_username)
How would I go about having it so whatever user has the same troop details will appear within the st_username_list as a choice?
After researching and trying things with the code, I have been getting:
ValueError
Cannot assign "'user1'": "stData.st_username" must be a "User" instance.
I hope this is not too confusing.
Edit
Ok so I have found that I can filter the options for the st_username by doing
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['st_username'].queryset = UserProfile.objects.filter(troop='''user's troop''')
Problem update
My main issue now is that I am unable to pass through the user instance within the model. I have seen this question here. So I added this to my form's innit method:
self.user = kwargs.pop('user')
Yet when I try and use the user by going self.user I get the an unhelpful error KeyError saying user. The shell indicated this may be due to the self.user = kwargs.pop(user)
I believe this may be because I am not passing through the user. So when I call the form in my views, I tried form = BadgeForm(user=request.user) and got the same error.
my queryset looks like this now:
self.fields['scout_username'].queryset=UserProfile.objects.filter(troop=user.userprofile.troop)
Further Information:
To understand the problem better, I have passed through a set variable of the troop within the queryset. So in this case
self.fields['scout_username'].queryset=UserProfile.objects.filter(troop='BC')
Although now I get Error AttributeError:
'BadgeForm' object has no attribute 'name'
The shell links this with the formset from which I use the form with. The details I'm provided is:
line 435, in formset_factory
return type(form.__name__ + 'FormSet', (formset,), attrs)
I hope this makes more sense to you than to me! Any help would be greatly appreciated!
The final problem was within the use of the formset.
According to the docs, the proper way to add the kwargs is to do such:
BadgeFormsSet = formset_factory(BadgeForm)
formset = BadgeFormsSet(form_kwargs={'user': request.user})
Hope this helps any one else!

NOT NULL contraint failed: campgrounds_campground.author_id

First go at building a Django app solo.
I've set up a user model as follows
class User(auth.models.User):
def __str__(self):
return self.username
Everything works fine there. I've then created a campground model
class Campground(models.Model):
author = models.ForeignKey('auth.User' , related_name='submitted_campgrounds')
name = models.CharField(max_length=128, blank=False, unique=True)
image = models.URLField(blank=False)
description = models.TextField()
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('campgrounds:single', kwargs={'pk':self.pk})
Edit
View Code
class CreateCampground(LoginRequiredMixin,generic.CreateView):
fields = ('name', 'image', 'description')
model = Campground
Form is coming from the CreateView.
I'm getting the error when submitting a form to create a new campground.
Shound't it be taking the user id from the logged in user? I do have the LoginRequiredMixin on the view for the create view.
Any help is appreciated
Your code seams to be fine. There might be an issue with existing user who does not have an object Campground as you just created new model Campground. I remember to fix this issue by logging to Django's admin, and manually create a Campground for any user who hasn't got Campground. Alternatively, you can try to delete that user, and makemigrations again.
Hope this help.
Cheers
Henry
Solved by adding this to my CreateCampground view
def form_valid(self, form):
form.instance.author = self.request.user
return super(CreateCampground, self).form_valid(form)

Like functionality in Django

I'm developing a social platform and currently coding the like functionality for user posts. However, I can't seem to make it work. These are my Models.py:
class Post(models.Model):
user = models.ForeignKey(User)
posted = models.DateTimeField(auto_now_add=True)
content = models.CharField(max_length=150)
picturefile = models.ImageField(upload_to="post_content", blank=True)
class Like(models.Model):
user = models.ForeignKey(User, null=True)
post = models.ForeignKey(Post, null=True)
I pass the post ID through my url as 'post_id', and then in my views:
def liking(request, post_id):
newlike = Like.objects.create()
newlike.post = post_id
newlike.user = request.user
newlike.save()
return redirect(reverse('dashboard'))
However, it returns the following error:
Cannot assign "'47'": "Like.post" must be a "Post" instance.
Does anyone knows what I'm missing or doing wrong?
You are passing newlike.post a number (integer field) while it is expecting a Post instance.
This sould work:
from django.http.shortcuts import get_object_or_404
def liking(request, post_id):
post = get_object_or_404(Post, id=post_id)
newlike = Like.objects.create(user=request.user, post=post)
return redirect(reverse('dashboard'))
Note 1: Better use the handy shortcut get_object_or_404 in order to raise a 404 error when the specific Post does not exist.
Note 2: By calling objects.create will automatically save into the db and return an instance!
newlike.post should be a Post object, not an int.
You need to find post by id first:
post = Post.objects.get(pk=post_id)
newlike.post = post
or, if you don't want to do this lookup:
newlike.post_id = post_id

Categories