I'm trying to add a commenting and replying system to my products model but I can't add replies to comment.
This is being done in the same page where the product details are being shown to the user.
Edit:
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())
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)
As the error message suggests, you're trying to assign a Product instance to a field that expects a Comment instance.
This is the line where you try to do this:
connected_replies = Reply.objects.filter(comment=self.get_object())
self.get_object() returns a Product instance as you defined model = Product on your View.
To get the replies connected to your product, you will need to loop over all comments and per comment all its replies as you defined these relations as foreignkeys.
For example:
for comment in connected_comments:
comment_replies = Reply.objects.filter(comment=comment)
#Vincent answer is ok, the error is from wrong model passed to filter of Replay model.
But for remedy to make it easier in template for showing comments and replies to those comments i suggest delete from context
data['replies']
data['no_of_replies']
and in template where you loop through comments (just example):
{% for comment in comments %}
<h1>{{comment}}</h1>
{% for reply in comment.reply_set.all %}
<p>{{ reply }} </p>
{% endfor %}
{% endfor %}
use reverse relationship with reply_set.
Oh, and for optimization add prefetch_related to your query:
Comment.objects.filter(product=self.get_object()).prefetch_related('reply_set')
Related
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.ForeignKey(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)
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
you can see my_post is select automatically
In your model the field name is my_post while in the add_price_to_post you are adding ps.post. change that to ps.my_post.
I have no idea if this question make much sense or not but i am so confused about it. I have a post list view and it is rendering some of the post here.
My question is how can I split the sections of the page.something like this.
what should be the approach of making this kind of view.
this is my posts view.py
posts/view.py
class PostListView(ListView):
model = Post
template_name = 'posts/home.html'
context_object_name = 'posts'
ordering = ['-date_posted']
def get_queryset(self):
if not self.request.user.is_authenticated:
return Post.objects.all()[:10]
else :
return super().get_queryset()
posts/models.py
from django.db import models
from django.utils import timezone
from slugger import AutoSlugField
from django.contrib.auth.models import User
from django.urls import reverse
# Create your models here.
def upload_location(instance, filename):
return "%s/%s" %(instance.slug, filename)
class Category(models.Model):
title = models.CharField(max_length= 60)
slug = AutoSlugField(populate_from='title')
parent = models.ForeignKey('self',blank=True, null=True ,related_name='children',on_delete=models.CASCADE)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
def __unicode__(self):
return self.title
def __str__(self):
return self.title
class Post(models.Model):
title = models.CharField(max_length=120)
slug = AutoSlugField(populate_from='title')
image = models.ImageField(
upload_to=upload_location,
null=True,
blank=True,
)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self, slug=None):
return reverse("posts-detail", kwargs={"slug": self.slug})
You have posts assigned to categories. Each post could be assigned only to one category (since you have FK from Post to Category). And you want to display all categories and 10 latest posts under each one.
I see several ways of how to solve that. The easiest one is to extend Category model with property, containing the queryset to retrieve related posts in the way you want them for front page.
class Post(models.Model):
title = models.CharField(max_length=255)
category = models.ForeignKey('Category', on_delete=models.CASCADE, related_name='posts')
date_posted = models.DateTimeField(default=timezone.now)
class Category(models.Model):
title = models.CharField(max_length=255)
#property
def posts_for_frontpage(self):
return self.posts.order_by('-date_posted')[:10]
class FrontpageView(ListView):
model = Category
template_name = 'frontpage.html'
context_object_name = 'categories'
def get_queryset(self):
# select some categories for frontpage
# all by default
return Category.objects.all()
and then in template
{% for category in categories %}
<h1>{{ category.title }}</h1>
<hr />
{% for post in category.posts_for_frontpage %}
<h4>{{ post.title }}</h4>
{% endfor %}
<br />
<br />
{% endfor %}
You could also play with select_related to reduce number of queries and with annotate to get all related posts.
Here is my models.py py file.
from django.db import models
from django.conf import settings
from django.urls import reverse
class Article(models.Model):
'''Modelling the article section.'''
title = models.CharField(max_length=200)
body = models.TextField()
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
'''Return string representation of the model.'''
return self.title
def get_absolute_url(self):
'''Return the url of this model.'''
return reverse('article_detail', args=[str(self.id)])
class Comment(models.Model):
'''Modelling the comment section.'''
article = models.ForeignKey(
Article,
on_delete = models.CASCADE,
related_name = 'comments'
)
comment = models.CharField(max_length=150)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
def __str__(self):
'''String representation of the model. '''
return self.comment
class Reply(models.Model):
''' Modelling the reply section. '''
comment = models.ForeignKey(
Comment,
on_delete = models.CASCADE,
related_name = 'replys'
)
reply = models.CharField(max_length=100)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
def __str__(self):
''' String representation of the model. '''
return self.reply
I need to access my Reply table in the Detail View template(Using generic view class DetailView). I have tried so far the following command in the template.
article.comments.replys.all
Its not able to retrive any data from Reply table. Thanks in advance.
article.comments is a manager; you need to iterate over it to get Comment instances. Each one will have .replys.
{% for comment in article.comments.all %}
{% for reply in comment.replys.all %}
...
{% endfor %}
{% endfor %}
I am new to Django I have two models are user and address, here user having two
foreign key fields are 'localaddress', 'permanentaddress'
Address model:
class Address(models.Model):
fulladdress = models.CharField(max_length=1000, null=True, blank=True)
additional_address = models.CharField(max_length=1000, null=True, blank=True)
street_address = models.CharField(max_length=150, null=True, blank=True)
route = models.CharField(max_length=150, null=True, blank=True)
city = models.CharField(max_length=100, null=True, blank=True)
state = models.CharField(max_length=100, null=True, blank=True)
country = models.CharField(max_length=100, null=True, blank=True)
pincode = models.IntegerField(null=True, blank=True)
class Meta:
db_table = 'address'
User model:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
localaddress = models.ForeignKey(Address, on_delete=models.CASCADE, related_name="localaddress", null=True, blank=True)
permanentaddress = models.ForeignKey(Address, on_delete=models.CASCADE, related_name="permanentaddress", null=True, blank=True)
class Meta:
db_table = 'user'
settings.py:
AUTH_USER_MODEL = 'student.User'# changes built-in user model to ours
here both localaddress and permanentaddress having same foreign key (Address model only)
Edit form:
forms.py:
class LocaladdressForm(forms.ModelForm):
class Meta:
model = Address
fields = ['fulladdress', 'additional_address', 'street_address', 'route', 'city', 'state', 'country', 'pincode']
def save(self, commit=True):
user = super(LocaladdressForm, self).save(commit=False)
user.fulladdress = self.cleaned_data['fulladdress']
user.additional_address = self.cleaned_data['additional_address']
user.street_address = self.cleaned_data['street_address']
user.route = self.cleaned_data['route']
user.city = self.cleaned_data['city']
user.state = self.cleaned_data['state']
user.pincode = self.cleaned_data['pincode']
user.country = self.cleaned_data['country']
if commit:
user.save()
return user
views.py:
def address_form(request):
if request.method == 'POST':
address = Address()
form = AddressForm(request.POST, instance=address)
if form.is_valid():
form.save()
userid = request.user.id
User.objects.filter(pk=userid).update(localaddress=address)
return redirect(reverse('student:view_profile'))
else:
args = {'form': form}
return render(request, 'student/addressform.html', args)
else:
form = AddressForm()
args = {'form': form}
return render(request, 'student/addressform.html', args)
i am using for loop to render form elements, i don't know where i did wrong
addressform.html:
<form method="post">
{% csrf_token %}
{{ form.fulladdress }}
{{ form.additional_address}}
{{ form.street_address}}
{{ form.street_address }}
{{ form.city }}
{{ form.state }}
{{ form.pincode }}
{{ form.country }}
</form>
Here i need to render one form localaddress and another form permanentaddress foreign key fields to my template. Initially i am trying first form (localaddress) please help me any one.
Thanks in advance ...
Localaddress form screenshot: Here i did it Autocomplete Address Form using google address api reference link here Autocomplete Address
I am able to persist address and user object but user object creating new object it is not persisting existing object (means localaddress)
I can answer partially about a concept in Django.
Whenever you create a relationship between two Django models you decide which model will be the main model. Over here you have User model and Address model. I am sure in most cases you will agree that User model is the main model and Address will be the sub-model or child model or whatever you call it.
So your ForeignKey field should ALWAYS be on the sub-model. So instead of using ForeignKey on User model like you have done you should do something like this:
address_type_choices = [
(1, 'Local'),
(2, 'Permanent'),
]
class Address(models.Model):
user = models.ForeignKey(User)
type = models.IntegerField(default=1, choices=address_type_choices)
house = model.CharField(max_length=50)
road = model.CharField(max_length=50)
area = model.CharField(max_length=50)
...
Please change the structure of your model and edit the question. I am sure that is what others will suggest too.
Remember, main model will not have foreign key field.
Update: Changed the model to allow you to maintain both local and permanent address.
I'm creating a page that has one video , as many comments , replies for each comment
I could retrieve video and comments but replies for each comment haven't been retrieved eventually.
I made some for loops in views file but didn't know also how to retrieve it in the templates file.
I'm stuck between views and templates till now
I'm using django 1.10.4
models.py
class Video(models.Model):
title = models.CharField(max_length=120)
embed_code = models.CharField(max_length=500)
slug = models.SlugField(null=True, blank=True)
category = models.ForeignKey("Category", null=True)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
active = models.BooleanField(default=True)
featured = models.BooleanField(default=False)
free_preview = models.BooleanField(default=False)
share_message = models.CharField(max_length=150, default=default_share_message)
objects = models.Manager()
# activemodel = ActiveModel()
featuresandactive = Features()
class Meta:
unique_together = ('slug', 'category')
def __str__(self):
return self.title
def get_absolute_url(self):
try:
return reverse('video_detail', kwargs={'vid_slug':self.slug, 'cat_slug':self.category.slug})
except:
return "/"
class Comment(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(MyUser)
path = models.CharField(max_length=350)
video = models.ForeignKey(Video, null=True, blank=True)
text = models.TextField()
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
Timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
active = models.BooleanField(default=True)
objects = CommentManager()
def __str__(self):
return self.text
class Reply(models.Model):
user = models.ForeignKey(MyUser)
comment = models.ForeignKey(Comment,null=True, blank=True)
text = models.TextField()
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
Timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
active = models.BooleanField(default=True)
objects = ReplyManager()
def __str__(self):
return self.text
views.py
def video_detail(request, cat_slug, vid_slug):
cat = Category.objects.get(slug=cat_slug)
comments = Comment.objects.filter(video=obj)
replys = Reply.objects.filter(comment=comments)
context = {
"cat": cat,
"obj":obj,
"comments":comments,
"replys":replys,
}
return render(request, 'video_detail.html', context)
this is another view.py
I tried this also but didn't work
def video_detail(request, cat_slug, vid_slug):
cat = Category.objects.get(slug=cat_slug)
obj = Video.objects.get(slug=vid_slug)
comments = obj.comment_set.all()
Replies = Reply.objects.filter(comment_id=comments.id))
context = {
"cat": cat,
"obj":obj,
"comments":comments,
"replies":replies
}
return render(request, 'video_detail.html', context)
IMO, you can represent the comments in your template. your template may look like this.
# your_template.html
{% for comment in obj.comment_set.all %}
{{comment.user}}: {{comment.text}}
{% for reply in comment.reply_set.all %}
{{reply.user}}: {{reply.text}}
{% endfor %}
{% endfor %}
then your view function only needs cat and obj, not comments or replies. also check out select_related.