I'm just starting to learn Django and building a simple blog with it.
So i have two models Post and PostStatistics. When ever i add a new post, i want that PostStatistics contains all specified default values. How can i achieve this correctly?
models.py
class PostStatistics(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
post_views = models.IntegerField(default=0)
post_likes = models.IntegerField(default=0)
post_favorites = models.IntegerField(default=0)
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
user = models.ForeignKey(UserProfile, null=True, on_delete=models.CASCADE)
statistics = models.ForeignKey(PostStatistics, null=True, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
body = RichTextField(blank=True, null=True)
draft = models.BooleanField(default=False)
views.py
def add_post(request: HttpRequest):
form = PostForm(request.POST)
is_draft = True if form.data.get("draft") == "on" else False
post = Post(
title=form.data["title"],
body=form.data["post"],
user=request.user,
draft=is_draft,
statistics = PostStatistics() -> this is not correct
)
post.save()
return redirect("post")
At the moment i get FOREIGN KEY constraint failed.
You create a new one:
def add_post(request: HttpRequest):
form = PostForm(request.POST)
is_draft = form.data.get('draft') == 'on'
post_statistics = PostStatistics.objects.create()
Post.objects.create(
title=form.data['title'],
body=form.data['post'],
user=request.user,
draft=is_draft,
statistics = post_statistics
)
return redirect('post')
It however does not make much sense to store the statistics in a separate model, since there is a clear one-to-one relation, and thus the statistics can be stored in the Post model.
Furthermore you can use the form to validate the input and also create the object (or at least parts of it). A better modeling thus might be:
from django.conf import settings
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE, editable=False)
title = models.CharField(max_length=200)
body = RichTextField(blank=True, null=True)
draft = models.BooleanField(default=False)
post_views = models.IntegerField(default=0, editable=False)
post_likes = models.IntegerField(default=0, editable=False)
post_favorites = models.IntegerField(default=0, editable=False)
and then work with a ModelForm where you let the form do all the proper validation and cleaning:
from django.contrib.auth.decorators import login_required
#login_required
def add_post(request: HttpRequest):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.instance.user = request.user
form.save()
return redirect('post')
else:
form = PostForm()
return render(request, 'name-of-some-template.html', {'form': form})
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
Related
I'm getting a Cannot assign "<Product: Test Product>": "Reply.comment" must be a "Comment" instance. error at new_reply = Reply(content=content, author=self.request.user, comment=self.get_object()) and don't know what to do to fix it.
views.py:
class ProductFeedbackView(DetailView):
model = Product
template_name = 'store/product_feedback.html'
def get_context_data(self , **kwargs):
data = super().get_context_data(**kwargs)
connected_comments = Comment.objects.filter(product=self.get_object())
number_of_comments = connected_comments.count()
data['comments'] = connected_comments
data['no_of_comments'] = number_of_comments
data['comment_form'] = CommentForm()
connected_replies = Reply.objects.filter(comment=self.get_object())
number_of_replies = connected_replies.count()
data['replies'] = connected_replies
data['no_of_replies'] = number_of_replies
data['reply_form'] = ReplyForm()
return data
def post(self , request , *args , **kwargs):
if self.request.method == 'POST':
reply_form = ReplyForm(self.request.POST)
if reply_form.is_valid():
content = reply_form.cleaned_data['content']
new_reply = Reply(content=content, author=self.request.user, comment=self.get_object())
new_reply.save()
return redirect(self.request.path_info)
if self.request.method == 'POST':
comment_form = CommentForm(self.request.POST)
if comment_form.is_valid():
content = comment_form.cleaned_data['content']
new_comment = Comment(content=content, author=self.request.user, product=self.get_object())
new_comment.save()
return redirect(self.request.path_info)
models.py:
class Product(models.Model):
author = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
title = models.CharField(max_length=120, unique=True)
description = models.CharField(max_length=300, blank=True, null=True)
class Comment(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, blank=True, null=True, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True,)
content = models.CharField(max_length=200, null=True, blank=False)
class Reply(models.Model):
comment = models.ForeignKey(Comment, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True,)
content = models.TextField(null=True, blank=False)
It's the same error as I pointed out in the other question you asked. Here is the line of code that is not working:
new_reply = Reply(content=content, author=self.request.user, comment=self.get_object())
The cause of this is also the same as your other question:
self.get_object() returns a Product instance as you defined model = Product on your View.
To fix this, you'll need to get the comment in your post method before creating the reply.
One solution for this could be posting the comment id with the reply data, for example as a hidden field. This id can then be used to get the comment instance where this reply belongs to. If you choose this solution, also make sure the comment actually belongs to this product as you don't want someone to just change the value of the comment id and be able to reply to any other comments.
Some additional feedback:
You don't need to check for the request method in the post method as it's already the post method.
You're now trying to save both a comment and reply whenever a post is done. You should either create different views for each post or add an if statement to check which form is posted. For example also using a hidden field with a value for that.
The comment model defines a product and an author relation. Both being null=True and blank=True. I think you always want a comment to belong to a product and an author. So you should remove those. Same for reply.
After a successful post, use a HttpResponseRedirect where you reverse the url of the product.
When I do this exactly as provided below, a shipping address object is created without the customer assigned in the shipping address foreignkey field, I can add it from the admin panel manually but I'm not able to make it work through code
**models.py**
class Customer(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE, blank=True, null=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(max_length=150)
class ShippingAddress(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, blank=True, null=True)
address_one = models.CharField(max_length=200)
address_two = models.CharField(max_length=200)
...
**views.py**
def checkout(request):
if request.method == 'POST':
form = ShippingForm(request.POST)
customer = request.user.customer
if form.is_valid():
# how to add the customer object to the foreignkey field of the shipping address
form.save()
return redirect('store:checkout_shipping')
else:
form = ShippingForm()
else:
form = ShippingForm()
context = {"form": form}
return render(request, 'store/checkout.html', context)
ShippingAddress.objects.get(customer=customer)
This returns a ShippingAddress, but
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE, blank=True, null=True)
requires a CustomUser. These are incompatible, so you cannot assign them.
But you are already getting the user:
customer = request.user.customer
Just reduce this a little:
user = request.user
now you have a user object.
I am assuming that you have correctly set up the CustomUser class in the Django settings.
I am trying to save an object to my database while adding it to the Many to Many field of another object. I already tried many other solutions from here but nothing worked so far.
Model:
class SellerPost(models.Model):
post_uuid = models.UUIDField(default=uuid.uuid4, editable=False)
seller = models.ForeignKey("User", on_delete=models.CASCADE)
text_content = models.TextField()
comments = models.ManyToManyField("SellerPostComment", blank=True)
class SellerPostComment(models.Model):
comment_id = models.IntegerField(primary_key=True)
post = models.ForeignKey(SellerPost, on_delete=models.CASCADE)
addressed = models.ForeignKey("User", on_delete=models.CASCADE, null=False, related_name="seller_addressed_comment")
commenter = models.ForeignKey("User", on_delete=models.CASCADE, null=False)
content = models.TextField()
View (i cut everything but the essential part that has sth to do with the error):
post = request.POST["post"]
post_obj = SellerPost.objects.get(post_uuid=post)
comment = comment_form.save(commit=False)
comment.addressed = user
comment.commenter = request.user
comment.post = post_obj
comment.save()
post_obj.comments.add(comment)
return redirect(index)
class PostCommentForm(forms.ModelForm):
class Meta:
model = SellerPostComment
fields = ("content",)
def save(self, commit=True):
comment = super(PostCommentForm, self).save(commit=False)
if commit:
comment.save()
return comment
Error:
Cannot add "<SellerPostComment: SellerPostComment object (None)>": the value for field "sellerpostcomment" is None
The form is valid but it just won't save the comment to the M2M field of the post.
Thanks in advance!
hi am working on a project where am using multiple user data
a user did a post onto the site and when driver see that post he adds their offer to that post but when driver submit the post ...at the admin level the particular is selected automatically but the post is not selected on which he adds price
this is my post model.py
class Loader_post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE ,related_name="Loader")
pick_up_station = models.CharField(max_length=150)
destination_station = models.CharField(max_length=150)
sender_name = models.CharField(max_length=150)
phone_number = PhoneNumberField(null=False, blank=False, unique=True)
receiver_name = models.CharField(max_length=150)
this is my second model of adding price to a particular post
class price(models.Model):
my_post = models.ManyToManyField(Loader_post, related_name='prices')
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, default='')
driver_price = models.CharField(max_length=150, null=True)
driver_name = models.CharField(max_length=150, null=True)
approved_price = models.BooleanField(default=False)
status = models.BooleanField(default=False)
this is my adding price to the post views.py
#login_required
def add_price_to_post(request, pk):
post = get_object_or_404(Loader_post, pk=pk)
user = request.user
if request.method == "POST":
form = price_form(request.POST)
if form.is_valid():
ps = form.save(commit=False)
ps.user = request.user
ps.status = True
ps.post = post
ps.save()
return redirect('Driver:Driverview')
else:
form = price_form()
return render(request, 'price_form.html', {'form': form})
this is my html add post button
{% for loader in Loader %}
this is loop
and this is button
add price
It is a ManyToMany relation between price and Loader_post model, hence ps.post = post won't work. You need to use add() method to add new post. Like this:
ps.save()
ps.my_post.add(post)
So I know there are a few similar questions but none of the solutions worked for me. I've tried save(commit=false) and save_m2m as well as a bunch of other stuff but i get the error
NOT NULL constraint failed: home_services.managers_id
Anyways here's my code:
views.py
def service(request):
if request.user.is_authenticated:
if request.method == 'POST': #Create Service
form = CreateServiceForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/service') #Later change this to redirect to server page
else:
form = CreateServiceForm()
args = {'user': request.user, 'form': form}
return render(request, 'service.html', args)
else:
return HttpResponseRedirect('/feed')
models.py
class Services(models.Model):
name = models.CharField(max_length=100, default='')
description = models.CharField(max_length=500, default='')
owner = models.OneToOneField(User, on_delete=models.CASCADE)
managers = models.ForeignKey(User, related_name="managers", on_delete=models.CASCADE)
members = models.ManyToManyField(User, related_name="members")
def __str__(self):
return str(self.name) + ": id" + str(self.id)
forms.py
class CreateServiceForm(forms.ModelForm):
owner = forms.ModelChoiceField(queryset=User.objects.all())
members = forms.ModelMultipleChoiceField(queryset=User.objects.all())
class Meta:
model = Services
fields = [
'name',
'description',
'owner',
'members',
]
I want the user to be able to create a service and select 1 or more members that is in the default django User model.
I want the user to be able to create a service and select 1 or more members that is in the default django User model.
You are not providing managers, which is not allowed as per the current configuration.
So, change:
managers = models.ForeignKey(
User,
related_name="managers",
on_delete=models.CASCADE,
)
To:
managers = models.ForeignKey(
User,
related_name="managers",
on_delete=models.CASCADE,
blank=True,
null=True,
)
You might want to read more about blank and null.