NOT NULL contraint failed: campgrounds_campground.author_id - python

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)

Related

How to obtain a user instance in django application

I'm trying to obtain a user instance for the profile page in django app but I'm finding some difficulties implementing that functionality. I have the following blocks of code:
models.py
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, primary_key=True, on_delete=models.CASCADE, default="")
image = models.ImageField(upload_to="images/user_profile_pics/", default="images/default_profile_pics/default.jpg")
firstname = models.CharField(max_length=50)
lastname = models.CharField(max_length=50)
def __str__(self):
return f'{self.lastname} profile'
serializers.py
class user_profile_serializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
views.py
class user_profile(generics.GenericAPIView):
serializer_class = user_profile_serializer
def get(self, request, *args, **kwargs):
user = self.get_serializer(request.user).data
if request.user.is_authenticated:
return Response(user, status=status.HTTP_200_OK)
else:
pass
urls.py
urlpatterns = [
path('profile/', user_profile.as_view(), name="user-profile"),
]
When ever I assess the profile url, I get an error message 'AnonymousUser' object has no attribute 'data' I have tried a couple of approaches but none worked. Please, how do I obtain a specific user from the database?
request.user is AnonymousUser when the user is not logged in. In that case that object does not have data attribute. Hence the error you get. One thing you can do is check request.user.is_authenticated and if the user is not authenticated, return some other value / or None. And try logging in before trying to access the user.data value.

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)

Saving Form Data with a ForeignKey Field

I want to expand my User Model with a UserProfile model. This UserProfile model includes a ForeignKey Field. In the form, I would like to use a ModelChoiceField to pre-populate this form field.
Whenever I submit the form, I get
ValueError at /accounts/register/
Cannot assign "'13'": "UserProfile.course" must be a "Course" instance.
Any help would be appreciated!
My Code:
models.py
class Course(models.Model):
course_accid = models.CharField(max_length=10)
def __str__(self):
return self.course_accid
class UserProfile(models.Model):
# This line is required. Links UserProfile to a User model instance.
user = models.OneToOneField(User)
website = models.URLField(blank=True)
picture = models.ImageField(upload_to='profile_images', blank=True)
course = models.ForeignKey(Course)
def __unicode__(self):
return self.user.username
def user_registered_callback(sender, user, request, **kwargs):
profile = UserProfile(user = user)
profile.website = request.POST["website"]
profile.course = Course.objects.get(pk=request.POST["course"]),
profile.save()
forms.py
class RegistrationForm(RegistrationForm):
course = forms.ModelChoiceField(queryset=Course.objects.all())
website = forms.URLField()
So, the problem that's occurring is that course needs to be set to a course instance with a step before, on forms.py, before it's a ModelChoiceField. The reason why is because querying it, like you're doing with queryset is really just searching for a string that matches, not the actual object.
If you break it up into two steps,
class = [some_method_for_getting_a_class_object]
UserProfile.class = class
Then it should get rid of that error.

ValueError: Lookup failed for model referenced by field

I have made Custom User model in my Django project. Here it is:
class CustomUser(User):
avatar = models.ImageField(upload_to='avatars')
about_myself = models.TextField(max_length=300)
USERNAME_FIELD = 'username'
def __str__(self):
return self.username
def is_author(self):
return 'blog.change_post' and 'blog.add_post' in self.get_all_permissions()
And after it, I changed all Foreign Keys of user to new CustomUser model. It works OK. But I make one new migration and django cause error, when I want to migrate it:
ValueError: Lookup failed for model referenced by field blog.Comment.author: main.CustomUser
My blog.Comment model:
class Comment(models.Model):
content = models.TextField()
author = models.ForeignKey(CustomUser)
date_create = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey(Post)
What should I do?
Thanks!
Judging from the code you posted, you might be might be better served by extending the user model rather than replacing it. This pattern is usually called a profile model and works via a one-to-one relationship with User.
Profiles provides application specific fields and behaviors, while allowing User to go about it's usual business unchanged. It doesn't require you to muck around with rewriting auth or even necessarily change your foreign keys.
Here's an example of your code written as a profile:
class Profile(models.Model):
# Link to user :
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to='avatars')
about_myself = models.TextField(max_length=300)
def __str__(self):
return self.user.username
def is_author(self):
return 'blog.change_post' and 'blog.add_post' in self.user.get_all_permissions()
Comment model:
class Comment(models.Model):
content = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL)
date_create = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey(Post)
# How to access the profile:
def check_author(self):
self.author.profile.is_author()
You'll also want to add a signal to create a new profile when a user is registered:
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_profile_for_new_user(sender, created, instance, **kwargs):
if created:
profile = Profile(user=instance)
profile.save()
Django docs on extending users.
If a profile approach doesn't work for you, try inheriting from AbstractUser or AbstractBaseUser instead of User. The abstract models provide the same basic functionality as User and are the preferred technique for recent Django versions.
There are a handful of additional steps however, check out the docs on creating custom users for a run down.

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

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)

Categories