I am currently working on a blog for a project, and i want to add in a comment section for each post in the DetailView. My code seems fine, however i keep getting a "Page not Found(404) error". I am thinking it may be my url pattern but i cannot seem to figure out what i am doing wrong.
error given
url.py
urlpatterns[
path('blog/post/<int:pk>/', views.PostDetailView.as_view(),name='post_detail'),
path('blog/post/<int:pk>/comment/', views.MyFormView.as_view(), name='my_form_view_url'),
]
forms.py
class CommentForm(forms.ModelForm):
class Meta():
model = Comment
fields = ('text',)
widgets = {
'text': forms.Textarea(attrs={'class':'ediable medium-editor-textarea'})
}
views.py
class PostDetailView(DetailView):
model = Post
template_name = 'carsforsale/post_detail.html'
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['form'] = CommentForm()
return context
class MyFormView(SingleObjectMixin,FormView):
template_name = 'carsforsale/post_detail.html'
form_class = CommentForm
model = Comment
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super().post(request, *args, **kwargs)
def get_success_url(self):
return reverse('carsforsale:post_detail', kwargs={'pk': self.object.pk})
post_detail.html
this is the form inside of the post_detail.html
<form method="POST" action="{% url 'carsforsale:my_form_view_url' pk=post.pk %}">
{% csrf_token %}
<div class="form-group">
{% render_field form.text class="form-control text" rows=3 %}
</div>
<input type="submit" class="btn btn-primary" value="Comment" />
</form>
These are my Post and Comment models
class Post(models.Model):
author = models.ForeignKey('auth.User', on_delete= models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def approve_comments(self):
return self.comments.filter(approved_comment=True)
def get_absolute_url(self):
return reverse("post_list")
def __str__(self):
return self.title
class Comment(models.Model):
post = models.ForeignKey('carsforsale.Post', related_name='comments', on_delete = models.CASCADE)
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def get_absolute_url(self):
return reverse("carsforsale:post_detail",kwargs={'pk':self.pk})
def __str__(self):
return self.text
Presumably the pk in the URL for MyFormView refers to the post the comment is going to be attached to. But the view itself has a model attribute of Comment, so that is the model that Django tries to load. The comment with that pk doesn't exist, hence the error.
You will need to override get_object to do the query directly and return the object. You will also need to override form_valid to associate the new comment with that post.
def get_object(self, **kwargs):
return get_object_or_404(Post, pk=self.kwargs["pk"]
def form_valid(self, form):
form.instance.post = self get_object()
return super().form_valid(form)
Related
When I query all the comments of the post, I want to return the user's username.
My two Models:
class Comment(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
post = models.ForeignKey(
Post, on_delete=models.CASCADE, null=False, blank=False)
title = models.TextField()
date = models.DateField(auto_now=True)
class User(AbstractUser):
objects = UserManager()
username = models.CharField(max_length=60, unique=True)
avi_pic = models.ImageField(
_('avi_pic'), upload_to=aviFile, null=True, blank=True)
My Comments Serializer:
class CommentSerializer(serializers.ModelSerializer):
username = serializers.SerializerMethodField('get_username_from_user')
avi_pic = serializers.SerializerMethodField('get_avi_pic')
class Meta:
model = Comment
fields = '__all__'
def get_username_from_user(self, comment):
username = comment.user.username
return username
def get_avi_pic(self, comment):
request = self.context['request']
avi_pic = comment.user.avi_pic.url
return request.build_absolute_uri(avi_pic)
My Comments View:
class CommentView(APIView):
authentication_class = [authentication.TokenAuthentication]
permission_class = [permissions.IsAuthenticated]
serializer_class = CommentSerializer
# Get all comments from current post
def get(self, request):
post_id = request.data.get('id')
post = Post.objects.get(id=post_id)
comment = Comment.objects.filter(post=post).values()
serializer = CommentSerializer(comment)
return Response(serializer.data, status=status.HTTP_200_OK)
In my console I get: 'QuerySet' object has no attribute 'user'
Appreciate any help!!
In views.py:
comment = Comment.objects.filter(post=post)
In serializer.py:
def get_username_from_user(self, comment):
username = comment.user.username
return username
In views.py:
def get(self, request):
...
serializer = CommentSerializer(comment, many=True)
...
In my humble opinion, your problem is not having a ForeignKey for the "User" model, meaning whatever model you are trying to render doesn't have a column named 'user'. I'd do something like this:
models.py
class User(AbstractUser):
pass
def __str__(self):
return f"{self.username}"
class Comment(models.Model):
comment = models.TextField(max_length=300, null=True)
creation_date = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE)
whatever_name = models.ForeignKey(whatever_model_to_relate, on_delete=models.CASCADE, related_name="comments")
forms.py
class CommentForm(ModelForm):
class Meta:
model = Comment
fields = ['comment']
widgets = {
'comment': forms.Textarea(attrs={'rows':4, 'cols':100}),
}
views.py
#login_required
def whatever_function(request, id):
whatever_name = whatever_related_model.objects.get(id=id)
return render(request, "template.html", {
"whatever_name_for_template": whatever_name,
"commentform": CommentForm()
})
template.html
{% for comment in whatever_related_model.comments.all %}
<div class="card p-1 m-2 col-lg-12 col-sm-12">
<div class="card-body">
<h5 class="card-title">{{ comment.user }}</h5>
<h6 class="card-subtitle mb-2 text-muted">{{ comment.creation_date }}</h6>
{{ comment.comment }}
</div>
</div>
{% endfor %}
Hopefully I didn't get sidetracked from your question.
I am new to django, and I am creating a vacation application. I want to be able to when I create a new trip, the user that created the trip becomes a member of that trip.
here is my models.py file:
class Trip(models.Model):
trip_name = models.CharField(max_length=255,unique=False)
start_date = models.DateField(default=datetime.date.today)
end_date = models.DateField(default=datetime.date.today)
slug = models.SlugField(allow_unicode=True,unique=True)
members = models.ManyToManyField(User,through='TripMember')
def __str__(self):
return self.trip_name
def save(self,*args,**kwargs):
self.slug = slugify(self.trip_name)
super().save(*args,**kwargs)
def get_absolute_url(self):
return reverse('trips:single',kwargs={'slug':self.slug})
class Meta:
ordering = ['start_date']
class TripMember(models.Model):
trip = models.ForeignKey(Trip,null=True,related_name='memberships',on_delete=models.SET_NULL)
user = models.ForeignKey(User,null=True,related_name='user_trips',on_delete=models.SET_NULL)
def __str__(self):
return self.user.username
class Meta:
unique_together = ('trip','user')
this is my forms.py file:
class TripCreateForm(forms.ModelForm):
class Meta:
fields = ('trip_name','start_date','end_date')
model = Trip
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["trip_name"].label = "Trip Name"
self.fields["start_date"].label = "Start Date"
self.fields["end_date"].label = "End Date"
here is my views.py file:
class CreateTrip(CreateView):
form_class = TripCreateForm
template_name = 'trips/trip_form.html'
and my trip_form.html page:
<form action="{% url 'trips:create' %}" method="post" id='tripForm'>
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" class="btn btn-primary btn-large" value="Create">
</form>
Where would I put the code to set the user as a tripmember and why?Also, if there is a better way to have set this up please let me know! I was gonna put it in the save part of the model but I am not quite sure if that is correct. Thanks!
You can override the form_valid() method of the CreateTrip class in your view:
def form_valid(self, form):
"""If the form is valid, save the associated model."""
self.object = form.save()
# add the current user to the members list of the trip
user = self.request.user
self.object.members.add(user)
return super().form_valid(form)
I want to deal with foreignKey with a modelForm and it raise this error:
ValueError at /3/
The view main.views.ProductDetailView didn't return an
HttpResponse object. It returned None instead.
the problem is on the Wilaya and commune fields, because when i remove them all works fine.
in views.py
class ProductDetailView(ModelFormMixin, DetailView):
model = Produit
context_object_name = 'produit'
form_class = CheckoutCreateForm
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context["form"] = self.get_form()
context["wilayas"]= Wilaya.objects.all()
context["communes"]= Commune.objects.all()
return context
def post(self, request, *args, **kwargs):
# self.object = self.get_object()
# form = self.get_form()
form = CheckoutCreateForm(request.POST)
if form.is_valid():
checkout = form.save(commit=False)
checkout.produit = self.get_object()
checkout.prix = self.get_object().price
checkout.save()
wilaya = form.cleaned_data['wilaya']
commune = form.cleaned_data['commune']
quantity = form.cleaned_data['quantity']
nom_du_client = form.cleaned_data['nom_du_client']
prenom_du_client = form.cleaned_data['prenom_du_client']
adresse_du_client = form.cleaned_data['adresse_du_client']
print('jusque la cv')
try:
form = CheckoutCreateForm()
return redirect(f'/{self.get_object().pk}')
except:
return redirect('/')
in models.py
class Wilaya(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Commune(models.Model):
Wilaya = models.ForeignKey(Wilaya, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Checkout(models.Model):
produit = models.ForeignKey('Produit', on_delete=models.CASCADE)
prix = models.IntegerField(default=0)
nom_du_client = models.CharField(max_length=40)
prenom_du_client = models.CharField(max_length=40)
adresse_du_client = models.CharField(max_length=40)
date_added = models.DateTimeField(auto_now_add=True)
wilaya = models.ForeignKey(Wilaya, on_delete=models.SET_NULL, null=True, blank=True)
commune = models.ForeignKey(Commune, on_delete=models.SET_NULL, null=True, blank=True)
quantity = models.PositiveIntegerField(default=1)
confirmer = models.BooleanField(default=False)
def __str__(self):
return str(self.produit)
in forms.py
from django import forms
from .models import Checkout, Commune , Wilaya
class CheckoutCreateForm(forms.ModelForm):
class Meta:
model = Checkout
fields = ['nom_du_client', 'prenom_du_client','adresse_du_client', 'quantity', 'wilaya', 'commune']
in html file :
<form class="card-body" method='POST' id="checkoutForm" >
{% csrf_token %}
<!--Grid row-->
<div class="row">
<!--Grid column-->
<div class="col-md-6 mb-2">
<!--firstName-->
<div class="md-form ">
<input type="text" name="nom_du_client" id="firstName" class="form-control">
<label for="nom_du_client" class="">First name</label>
</div>
.... rest of the form
<button class="btn btn-primary btn-lg btn-block" type="submit">Continue to checkout</button>
</form>
The error is pretty self-explanatory:
The view main.views.ProductDetailView didn't return an HttpResponse object. It returned None instead.
If you look at the reduced version of your code:
class ProductDetailView(ModelFormMixin, DetailView):
def post(self, request, *args, **kwargs):
...
if form.is_valid():
...
try:
...
return redirect(f'/{self.get_object().pk}')
except:
return redirect('/')
You can notice that the method post() returns None if the form is not valid.
One possible solution is to have a return redirect(...) at the end as well:
class ProductDetailView(ModelFormMixin, DetailView):
def post(self, request, *args, **kwargs):
...
if form.is_valid():
...
try:
...
return redirect(f'/{self.get_object().pk}')
except:
pass
return redirect('/')
I am building a blog in django 3. I want to filter all posts by author and display the results in author page. I can't seem to figure what is wrong with my code. Please help. If i can get an explaination on what is wrong with my code, that will be appreciated.
Views.py
class AuthorPostListView(ListView):
model = Post
paginate_by = 5
template_name = 'author_post.html'
def get_queryset(self):
return Post.objects.filter(author = self.request.user).order_by('created_on').reverse()
def get_context_data(self):
context = super(AuthorPostListView, self).get_context_data(**kwargs)
context['authorpost_list'] = Post.objects.all()
return context
Models.py
class Post(models.Model):
title = models.CharField(max_length=200)
post_slug = models.SlugField(max_length = 200, unique = True)
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
last_modified = models.DateTimeField(auto_now = True)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name = 'blog_posts',
)
post_picture = models.ImageField(
upload_to = 'post_picture',
null = True,
blank = True,
default='/media/post_picture/2.jpg',
)
approved_comment = models.BooleanField(default=True)
tags = TaggableManager()
categories = models.ManyToManyField('Category', related_name = 'posts')
def approve(self):
self.approved_comment = True
self.save()
class Meta:
ordering = ['created_on']
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post_detail', args=[str(self.id)])
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
Template
{% for object in authorpost_list %}
{{ object.title }}
{% endfor %}
urls.py
urlpatterns = [
path('author_post/', views.AuthorPostListView.as_view(), name = 'author_posts'),
]
you are over-riding the actual view you wanted with get context data, so you can remove the get context method and call in the template as
{% for object in object_list %}
{{ object.title }}
{% endfor %}
or you can do
context['authorpost_list'] = Post.objects.filter(author = self.request.user).order_by('-created_on')
and call in template in the same way as you shown
I am getting an error - Reverse for 'add_comment' with keyword arguments '{u'slug': None}' not found. 1 pattern(s) tried: [u'blog/(?P\d+)/comment/$']
I added - app_name= 'blog' - to urls to solve the earlier error of namespace
but now I am stuck with a new error
my urls.py
url(r'^(?P<pk>\d+)/comment/$', views.add_comment, name='add_comment')
my views.py
def add_comment(request, slug):
post = get_object_or_404(Post, slug=slug)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('blog:post', slug=post.slug)
else:
form = CommentForm()
template = 'blog/add_comment.html'
context = {'form': form}
return render(request, template, context)
my models.py
class Post(models.Model):
title = models.CharField(max_length=200)
body = models.TextField()
date = models.DateTimeField()
slug = models.SlugField(max_length=250, blank=True, null=True)
def __unicode__(self):
return self.title
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
def get_absoulte_url(self):
return reverse('blog:post.html', args=[self.slug])
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments', blank=True,
null=True)
user = models.CharField(max_length=250)
email = models.EmailField()
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=False)
def approved(self):
self.approved = True
self.save()
def __str__(self):
return self.user
my template add_comment.html
{% extends "personal/header.html" %}
{% block content %}
<h1>Add new comment</h1>
<form method='POST'>{% csrf_token %}
{{ from.as_p }}
<button type='submit'>Submit</button>
</form>
{% endblock %}
This is coming from a page where you had given the link to add comment, most probably in the detailed blog/post page.
You are creating the add comment URL without passing the pk argument to it.
It should be something like.
Add Comment
But you have missed blog.pk or something like post.pk in creating URL.
Just do that and error will be resolved.