NOT NULL constraint failed when linking a model with User - python

I have a model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
first_name = models.CharField(max_length=30, default='anon')
last_name = models.CharField(max_length=30, default='anon')
interest = models.CharField(max_length=30, default='nothing')
def __str__(self):
return 'Username:%s' % self.user.username
class Post(models.Model):
title = models.TextField(default='No title')
text = models.TextField(max_length=220)
vote = models.IntegerField(default=0)
user_post = models.ForeignKey(User, related_name='post')
class Comments(models.Model):
name = models.TextField(default='Anon', null=True)
comment = models.TextField(max_length=2000, null=True)
post = models.ForeignKey(Post)
def __str__(self):
return 'comment:%s' % self.comment
In the post you can see I'm linking a post with the User. I read that you can access a users comments this way by using user.post_set.all(), so I gave it a try. When I attempt to migrate the change(adding the foriegn key in Post), I get an error.
django.db.utils.IntegrityError: NOT NULL constraint failed:
boardapp_post__new.user_post_id
I notice the post__new, so here is my view named new, which creates a post.
def new(request):
if request.method == 'POST':
post = Post.objects.create(title=request.POST.get('post_title', ''),
text=request.POST.get('post_text',''),vote=0
)
return HttpResponseRedirect(reverse('view_post', args=(post.id,)))
return render(request, 'new.html')
I'm new to creating users in django and am confused as to where not null consraint is failing. Thanks

When you migrate to your new database format, there should be a default User for the user_posts that already exist.
Django will try to fill this value for you in existing user_posts, but it doesn't know which value to chose, since null is not allowed in your current model.
Thus: you need to tell Django either 1) not to worry about Posts without a user (null = True) or 2) supply a default User, which is a bit harder (probably would require a function call that creates some dummy User on the fly).
So the easiest solution is to alter your Post model and change user_post:
user_post = models.ForeignKey(User, related_name='post', null=True)

It is failing because your model field is
user_post = models.ForeignKey(User, related_name='post')
it means every post will be assigned to a user(not null field).
From your view, you are not assigning any user to the post, so change your code to
post = Post.objects.create(title=request.POST.get('post_title', ''),
text=request.POST.get('post_text',''), vote=0, user_post=request.user
)

Related

Django - Query by foreign key in views

I am trying to make a button on the post that when a user cliks on it, is requesting to be added to the post as an attendance and then, the author of the post has to approve that request.
Models.py
class Attending(models.Model):
is_approved = models.BooleanField(default=False)
attending = models.ManyToManyField(User, related_name='user_event_attending')
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField(blank=True)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
attending = models.ForeignKey(Attending, on_delete=models.CASCADE, verbose_name='atending', null=True)
My problem here is that every time I writte a query for the button is giving me erros and I couldn`t figure it out how to get the reverse of the foreign key.
This is my code on my views.py
def request_event(request, pk):
previous = request.META.get('HTTP_REFERER')
try:
query = Attending.objects.get(pk=pk)
request_attending = query.post_set.add(request.user)
messages.success(request, f'Request sent!')
return redirect(previous)
except query.DoesNotExist:
return redirect('/')
Thank you very much for your help in advance!
This: query.post_set is just relationship. You cannot call method add just like that. You can add to ManyToMany relation and I believe you want to add user to Attending.attending field, not directly to Post object. Change that to:
...
query = Attending.objects.get(pk=pk)
query.attending.add(request.user)
messages.success(request, f'Request sent!')
....
| Update |
I think you should consider rearraning your relationships. If I understand your plan, you should go this way:
class Attending(models.Model):
...
attendant = models.ForeignKey(User, related_name='events_attending', on_delete=models.CASCADE)
post = models.ForeignKey('Post', on_delete=models.CASCADE)
class Post(models.Model):
...
author = models.ForeignKey(User, on_delete=models.CASCADE)
For one Post object there can be many Attending objects, then you can use relations like that:
att = Attending.objects.first()
att.post # get related Post object from ForeignKey | there is only one
post = Post.objects.first()
post.attending_set.all() # get all related Attending objects
Post.objects.get(attending=att) # get Post object that the Attending object have in ForeignKey field
user = User.objects.first()
user.post_set.all() # get all Post objects that User is author in
user.events_attending.all() # get all related Attending objects
For more check Django Docs.

Django - Annotate post query set data with if current user has "liked" the post

So I have this model
model.py
class Post(models.Model):
uuid = models.UUIDField(primary_key=True, default=generate_ulid_as_uuid, editable=False)
created = models.DateTimeField('Created at', auto_now_add=True)
updated_at = models.DateTimeField('Last updated at', auto_now=True, blank=True, null=True)
creator = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="post_creator")
body = models.CharField(max_length=POST_MAX_LEN, validators=[MinLengthValidator(POST_MIN_LEN)])
class LikePost(AbstractSimpleModel):
creator = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="like_post")
post = models.ForeignKey(Post)
class User(AbstractDatesModel):
uuid = models.UUIDField(primary_key=True)
username = models.CharField(max_length=USERNAME_MAX_LEN, unique=True, validators=[
MinLengthValidator(USERNAME_MIN_LEN)])
created = models.DateTimeField('Created at', auto_now_add=True)
updated_at = models.DateTimeField('Last updated at', auto_now=True, blank=True, null=True)
Then I also have this annotator for returning a bunch of data outside of the Post table
annotator.py
def query_to_full_post_data_serializer(post_query_set: QuerySet):
query_set_annotated = post_query_set.annotate(
creator_username=F('creator__username'),
user_liked=F(<flag for each post in query for if user liked the post>)
reply_count=Count('postreply', distinct=True),
like_count=Count('likepost', distinct=True),
).prefetch_related(
Prefetch('photo', Photo.objects.order_by('-created')),
Prefetch('video', Video.objects.order_by('-created'))
)
return FullPostDataSerializer(query_set_annotated, many=True)
I'd like to return a field called "user_liked", which returns a boolean for each post in a query set that is True if the current logged in user has liked it or is the creator of a LikePost to a Post. When the request comes in I get the current user making the request so I can get their uuid. I'd like to use that uuid to check if the user has liked a post in the query set. How do I check if the current logged in user has liked a Post object in a query set Django?
I'm assuming you can do something like user_liked=F('likepost', filter=creator__uuid=current_user_uuid), but that wouldn't be a boolean that'd return user. If I really wanted I could do user_liked=Count('likepost, filter=creator__uuid=current_user_uuid) seems kind of inefficient though.
Sorry for my previous wrong answer.
So I'm quite rusty in the Django ORM so that query might not have best optimisation. Here is what I have come up with
Post.objects.all().annotate(liked_by_user=Q(likepost__creator=user))
The issue being that it will add duplicates for each reverse relationship it finds. That's why you should not use that and use a many to many.
For example, with a many to many, it would be as simple as
class User2(models.Model):
_id = models.BigAutoField(primary_key=True)
class Post2(models.Model):
_id = models.BigAutoField(primary_key=True)
creators = models.ManyToManyField(User2)
and now you only need
Post2.object.all().annotate(liked_by_user=Q(creator__in=[user]))
which is way better.

How to insert data into a relational one to one table in django?

I have a UserProfile table which is in relation with the default Django User table. Here's how it looks.
class UserProfile(models.Model):
user = user.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)
I am trying to insert the data into the UserProfile table using the Django Shell.
from users.models import UserProfile
a = UserProfile(qrcode="hello")
a.save()
This is how I have always known to insert data into tables, it has always worked. BUT when i try to do this in UserProfile model. I get this exception. NOT NULL constraint failed: users_userprofile.user_id. Which in turn is caused by the following exception Error in formatting: RelatedObjectDoesNotExist: UserProfile has no user.
I somewhat understand that I somehow need to supply a user instance. But I am clueless as to how. Can someone please help me.
Firstly you need to create User.
u1 = User(username='user1')
u1.save()
Create a UserProfile. Pass the ID of the “parent” object as this object’s ID:
v1 = UserProfile(user=u1, ....)
v1.save()
refer this
You need to create your User first
user = User.objects.create(username='user')
and then you can do:
user_profile = UserProfile.objects.create(user=user, ...)

IntegrityError at /new_topic/ NOT NULL constraint failed: learning_logs_topic.owner_id

I am setting topics to have its user owner. But I wonder can I add a new_topic that does not have an owner by setting Null=True to some field?
class Topic(models.Model):
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.text
#login_required
def new_topic(request):
if request.method != 'POST':
form = TopicForm()
else:
form = TopicForm(request.POST)
if form.is_valid():
form.save()
The error said: Not NULL constraint failed. I know the solution as I changed form.save() to the following by set the new_topic with an owner:
new_topic = form.save(commit=False)
new_topic.owner = request.user
new_topic.save()
I cleaned all codes snippets having .owner suffix. I was guessing the problem is because as I Forignkey topic owner with User class, the User class now has a topic attribute(field) that sets Null = False by default So any new_topic should have a user as its owner. Here's what I did:
class Topic(models.Model):
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
def __str__(self):
return self.text
However, I still get the same error.. I guess that because ForeignKey field dose not support Null = True? or I wonder should I change User's topic field rather than Topic's owner field? if so How should I change User's default field? Thank you!
Please run python manage.py makemigrations and python manage.py migrate after you changed user to have null=True.

Django - saving data from form in database

I have just finished a Django tutorial for an email sign up form and I am trying to tweak some parts of the code to match my needs (adding an additional field to the sign up form)
Based on the tutorial I created an email signup Model form:
class JoinForm(forms.ModelForm):
class Meta:
model = Join
fields = ["email","wunschrad"]
and I altered it a little bit by adding the wunschrad field
then I updated my model to include wunschrad and synced the database with south, all working out fine. below my model:
class Join(models.Model):
email = models.EmailField()
wunschrad = models.CharField(max_length=120)
friend = models.ForeignKey("self", related_name='referral', null=True, blank=True)
ref_id = models.CharField(max_length=120, default='ABC', unique=True)
ip_address = models.CharField(max_length=120, default='ABC')
timestamp = models.DateTimeField(auto_now_add = True, auto_now = False)
updated = models.DateTimeField(auto_now_add = False, auto_now = True)
def __unicode__(self):
return "%s" % (self.email)
class Meta:
unique_together = ("email", "ref_id",)
Where I ran into problems is how do I adopt my view to reflect this change. Here is what I did (my changes highlighted in comments)
def home(request):
try:
join_id = request.session['join_id_ref']
obj = Join.objects.get(id=join_id)
except:
obj = None
form = JoinForm(request.POST or None)
if form.is_valid():
new_join = form.save(commit=False)
email = form.cleaned_data['email']
wunschrad = form.cleaned_data['wunschrad'] #I ADDED THIS
new_join_old, created = Join.objects.get_or_create(email=email, wunschrad=wunschrad) # I ADDED WUNSCHRAD HERE
if created:
new_join_old.ref_id = get_ref_id()
if not obj == None:
new_join_old.friend = obj
new_join_old.ip_address = get_ip(request)
new_join_old.save()
return HttpResponseRedirect("/%s" %(new_join_old.ref_id))
context = {"form": form}
template = "home.html"
return render(request, template, context)
My question/ problem is the following: This code snippet works but if a user signs up with the same email address and chooses once the first option of wunschrad and the second time the second option (for clarification: wunschrad is a drop-down list with two options) Django saves two versions in the database, like shown here: Django Admin Screenshot
Does anyone have an idea how to alter the code to save it only once per user?
many many thanks in advance for the help!
So your problem here is by adding wunschrad to get_or_create you are telling Django to look for an entry in the database that has both of those values instead of just the email. Changing it to this should fix it:
new_join_old, created = Join.objects.get_or_create(email=email)
new_join_old.wunschrad = wunschrad
new_join_old.save()
This will make sure it only creates a new object if the email isn't in the database already.
As a side note if you want your email to be unique and raise an error if you try to save another entry with the same email add unique=True to models.EmailField() like:
models.EmailField(unique=True)

Categories