I am trying to add comment to an Ad posted by a user. When I press the comment button, it is showing the error.
Here's my models.py
from django.db import models
from django.core.validators import MinLengthValidator
from django.contrib.auth.models import User
from django.conf import settings
class Ad(models.Model):
title = models.CharField(
max_length=200,
validators=[MinLengthValidator(2, "Title must be greater than 2 characters")]
)
price = models.DecimalField(max_digits=7, decimal_places=2, null=True)
text = models.TextField()
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
#picture
picture = models.BinaryField(null=True, blank=True, editable=True)
content_type = models.CharField(max_length=256, null=True, help_text='The MIMEType of the file')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
comments = models.ManyToManyField(settings.AUTH_USER_MODEL, through='Comment', related_name='comments_owned')
#show up in the admin list
def __str__(self):
return self.title
class Comment(models.Model) :
text = models.TextField(
validators=[MinLengthValidator(3, "Comment must be greater than 3 characters")]
)
ad = models.ForeignKey(Ad, on_delete=models.CASCADE)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# Shows up in the admin list
def __str__(self):
if len(self.text) < 15 :
return self.text
return self.text[:11] + ' ...'
Here's my forms.py
# commentForm
from django import forms
from ads.models import Ad, Comment
from django.core.files.uploadedfile import InMemoryUploadedFile
from ads.humanize import naturalsize
from django.core import validators
from django.core.exceptions import ValidationError
...
class CommentForm(forms.Form):
comment = forms.CharField(required=True, max_length=500, min_length=3, strip=True)
class Meta:
model = Comment
fields = ['text']
Here's my views.py
from ads.models import Ad, Comment
from ads.owner import OwnerListView, OwnerDetailView, OwnerCreateView, OwnerUpdateView, OwnerDeleteView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.views import View
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse_lazy, reverse
from django.http import HttpResponse
from ads.forms import CreateForm, CommentForm
class AdDetailView(OwnerDetailView):
model = Ad
def get(self, request, pk):
a = Ad.objects.get(id=pk)
comments = Comment.objects.filter(ad=a).order_by('-updated_at')
comment_form = CommentForm()
context = {'ad': a, 'comments':comments, 'comment_form':comment_form}
return render(request, 'ads/ad_detail.html', context)
....
class CommentCreateView(LoginRequiredMixin, View):
def post(self, request, pk):
ad = get_object_or_404(Ad, id=pk)
comment = Comment(text=request.POST['comment'], owner=request, ad=ad)
comment.save()
return redirect(reverse('ads:ad_detail', args=[pk]))
class CommentDeleteView(OwnerDeleteView):
model = Comment
def get_success_url(self):
ad = self.object.ad
return reverse('ads:ad_detail', args=[ad.id])
And finally my templates ad_detail.html
{% if user.is_authenticated %}
<br clear="all"/>
<p>
{% load crispy_forms_tags %}
<form method="post" action="{% url 'ads:ad_comment_create' ad.id %}">
{% csrf_token %}
{{ comment_form|crispy }}
<input type="submit" value="Submit">
<input type="submit" value="All Ads" onclick="window.location.href='{% url 'ads:all' %}'; return false;">
</form>
</p>
{% endif %}
{% for comment in comments %}
<p> {{ comment.text }}
({{ comment.updated_at|naturaltime }})
{% if user == comment.owner %}
<i class="fa fa-trash"></i>
{% endif %}
</p>
{% endfor %}
After clicking on the submit button, it's supposed to be taking to ads:all but instead throwing an error.
In case you also want to check urls.py
urlpatterns = [
path('', views.AdListView.as_view()),
path('ads', views.AdListView.as_view(), name='all'),
...
#comments
path('ad/<int:pk>/comment', views.CommentCreateView.as_view(), name='ad_comment_create'),
path('comment/<int:pk>/delete', views.CommentDeleteView.as_view(success_url=reverse_lazy('ads')), name='ad_comment_delete'),
]
You should be using ModelForm instead of Form here.
class CommentForm(forms.ModelForm):
comment = forms.CharField(required=True, max_length=500, min_length=3, strip=True)
class Meta:
model = Comment
fields = ['text']
And change your view
class CommentCreateView(LoginRequiredMixin, View):
def post(self, request, pk):
form = CommentForm(request.POST)
ad = get_object_or_404(Ad, id=pk)
comment = form.save(commit=False)
comment.ad = ad
comment.owner = request.user
comment.save()
return redirect('ads:ad_detail', ad.pk)
In you CommentCreateView, your owner is recieving request that was the reason you were shown that error. So:
comment = Comment(text=request.POST['comment'], owner=request.user, ad=ad)
Related
I'm working on my blog. I'm trying to list my latest posts in page list_posts.html.I tried but posts are not shown, I don't know why. I don't get any errors or anything, any idea why my posts aren't listed?
This is models.py
from django.db import models
from django.utils import timezone
from ckeditor.fields import RichTextField
from stdimage import StdImageField
STATUS = (
(0,"Publish"),
(1,"Draft"),
)
class Category(models.Model):
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created at")
updated_at = models.DateTimeField(auto_now=True, verbose_name="Updated at")
title = models.CharField(max_length=255, verbose_name="Title")
class Meta:
verbose_name = "Category"
verbose_name_plural = "Categories"
ordering = ['title']
def __str__(self):
return self.title
class Post(models.Model):
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created at")
updated_at = models.DateTimeField(auto_now=True, verbose_name="Updated at")
is_published = models.BooleanField(default=False, verbose_name="Is published?")
published_at = models.DateTimeField(null=True, blank=True, editable=False, verbose_name="Published at")
title = models.CharField(max_length=200, verbose_name="Title")
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey('auth.User', verbose_name="Author", on_delete=models.CASCADE)
category = models.ForeignKey(Category, verbose_name="Category", on_delete=models.CASCADE)
body = RichTextField(blank=True, null=True)
image = StdImageField(upload_to='featured_image/%Y/%m/%d/', variations={'standard':(1170,820),'banner':(1170,530),'thumbnail':(500,500)})
status = models.IntegerField(choices=STATUS, default=0)
class Meta:
verbose_name = "Post"
verbose_name_plural = "Posts"
ordering = ['-created_at']
def publish(self):
self.is_published = True
self.published_at = timezone.now()
self.save()
def __str__(self):
return self.title
This is views.py
from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from .models import Category, Post
def post_list(request):
posts = Post.objects.filter(published_at__lte=timezone.now()).order_by('published_at')
latest_posts = Post.objects.filter(published_at__lte=timezone.now()).order_by('published_at')[:5]
context = {'posts': posts, 'latest_posts': latest_posts}
return render(request, 'list_posts.html', context)
def post_detail(request, pk, post):
latest_posts = Post.objects.filter(published_at__lte=timezone.now()).order_by('published_at')[:5]
post = get_object_or_404(Post, pk=pk)
context = {'post': post, 'latest_posts': latest_posts}
return render(request, 'post_detail.html', context)
This is list_posts.html
{% extends "base.html" %}
{% load static %}
{% block content %}
<!-- Main Wrap Start -->
<main class="position-relative">
<div class="post-carausel-1-items mb-50">
{% for post in latest_posts %}
<div class="col">
<div class="slider-single bg-white p-10 border-radius-15">
<div class="img-hover-scale border-radius-10">
<span class="top-right-icon bg-dark"><i class="mdi mdi-flash-on"></i></span>
<a href="{{ post.get_absolute_url }}">
<img class="border-radius-10" src="{{ post.image.standard.url }}" alt="post-slider">
</a>
</div>
<h6 class="post-title pr-5 pl-5 mb-10 mt-15 text-limit-2-row">
{{ post.title }}
</h6>
<div class="entry-meta meta-1 font-x-small color-grey float-left text-uppercase pl-5 pb-15">
<span class="post-by">By {{ post.author }}</span>
<span class="post-on">{{ post.created_at}}</span>
</div>
</div>
</div>
{% endfor %}
</div>
</main>
{% endblock content%}
Everything works except that posts aren't listed. Why I don't get listed posts?
Thanks in advance!
The reason this doesn't work is because the published_at is apparently NULL and is thus never filled in. With the .filter(published_at__lte=timezone.now()), it checks that the published_at is less than or equal to the current timestamp. If it is NULL, it thus is excluded. That means that you will either need to fill in the published_at some way, or filter (and order) with a different field, like created_at. You can thus work with:
from django.db.models.functions import Now
from django.shortcuts import get_object_or_404, render
from .models import Category, Post
def post_list(request):
posts = Post.objects.filter(created_at__lte=Now()).order_by('-created_at')
latest_posts = posts[:5]
context = {'posts': posts, 'latest_posts': latest_posts}
return render(request, 'list_posts.html', context)
def post_detail(request, pk, post):
latest_posts = Post.objects.filter(created_at__lte=Now()).order_by(
'-created_at'
)[:5]
post = get_object_or_404(Post, pk=pk)
context = {'post': post, 'latest_posts': latest_posts}
return render(request, 'post_detail.html', context)
Note: You can work with Now [Django-doc] to work with the database timestamp instead. This can be useful if you want to specify the queryset in a class-based view, since each time the queryset is evaluated, it will then take the (updated) timestamp.
AttributeError at /blog/
Manager isn't accessible via Post instances
Error during template rendering
In template C:\Users\Mahdyar Eatemad\OneDrive\Projects\Gallery\templates\blog\post\list.html, error at line 33
Manager isn't accessible via Post instances
error
i used get_absolute_url to create slug for blog posts
This is my models
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from django.urls import reverse
# Create your models here.
class PublishedManager(models.Manager):
def get_queryset(self):
return super(PublishedManager, self).get_queryset().filter(status='published')
class Post(models.Model):
STATUS_CHOICES = (
('draft', 'چرک نویس'),
('published', 'منتشر شده'),
)
title = models.CharField(max_length=100)
slug = models.SlugField(max_length=100, unique_for_date='publish', allow_unicode=True)
body = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
bolded_text = models.TextField(blank=True)
picture = models.ImageField(upload_to='photos', blank=True)
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
objects = models.Manager()
published = PublishedManager()
class Meta:
ordering = ('-publish',)
def get_absolute_url(self):
return reverse('blog:post_detail', args=[self.published.year, self.published.month, self.published.day, self.slug])
def __str__(self):
return self.title
my views
from django.shortcuts import render, get_object_or_404
from .models import Post
# Create your views here.
def post_list(request):
posts = Post.published.all()
return render(request, 'blog/post/list.html', {'posts': posts})
def post_detail(request, year, month, day, slug):
post = get_object_or_404(Post, slug=slug, status='published', published__year=year, published__month=month, published__day=day)
return render(request, 'blog/post/list.html', {'post': post})
my url
from django.urls import path, register_converter
from django.urls.converters import SlugConverter
from . import views
app_name = 'blog'
class PersianSlugConvertor(SlugConverter):
regex = '[-a-zA-Z0-9_ضصثقفغعهخحجچگکمنتالبیسشظطزژرذدپوءآ]+'
register_converter(PersianSlugConvertor, 'persian_slug')
urlpatterns = [
path('', views.post_list, name='post_list'),
path('<int:year>/<int:month>/<int:day>/<persian_slug:slug>', views.post_detail, name='post_detail'),
]
template
{% for post in posts %}
<article class="post">
<div class="post_header">
<h3 class="post_title">{{ post.title }}</h3>
<div class="post_sub"><i class="fa-time"></i>منتشر شده در {{ post.publish }} توسط {{ post.author }}<i class="fa-comments-alt"></i> 6 نظر </div>
</div>
<div class="post_content">
<figure><img alt="عکس پست" src="{{ post.picture }}"></figure>
<p>{{ post.body|truncatewords:30|linebreaks }}</p>
بیشتر بخوانید </div>
</article>
{% endfor %}
you write
reverse('blog:post_detail', args=[self.published.year,
self.published.month, self.published.day, self.slug])
where you are referencing published which is a manager, instead you want to refer to publish which is your date time field
I'm trying to incorporate Select2 into my django form -- specifically ModelSelect2MultipleWidget -- so that the user can associate multiple event objects to another model (like CheckboxSelectMultiple). The associated models are:
from django.contrib.auth import get_user_model
from django.db import models
class F4Events(models.Model):
name = models.CharField(max_length=300)
handling_type = models.ManyToManyField(WaferHandlingType)
def __str__(self):
return self.name
class ToolPm(models.Model):
ceid = models.ForeignKey(CEID, on_delete=models.CASCADE)
name = models.CharField(max_length=250)
wafer_handling = models.BooleanField(default=True)
wafer_based = models.BooleanField(default=False)
frequency = models.FloatField(blank=True, null=True)
handling_type = models.ManyToManyField(WaferHandlingType, blank=True)
user_edited = models.BooleanField(default=False)
pm_f4_events = models.ManyToManyField('F4Events', blank=True)
def __str__(self):
return str(self.name) if self.name else ''
class Meta:
ordering = ('ceid', 'name')
My forms.py file is:
from django import forms
from .models import Entity, ToolPm, CEID, PDL, F4Events, WaferHandlingType
from django_select2 import forms as s2forms
class F4EventWidget(s2forms.ModelSelect2MultipleWidget):
search_fields = [
'name__icontains',
]
attrs = {'data-placeholder': 'Please Choose F4 Events'}
class PMForm(forms.ModelForm):
class Meta:
model = ToolPm
fields = ['wafer_handling', 'wafer_based', 'handling_type', 'pm_f4_events']
widgets = {'handling_type': forms.CheckboxSelectMultiple,
'pm_f4_events': F4EventWidget
}
my view.py is:
from datetime import datetime, timedelta
from django.db.models import Sum
from django.http import JsonResponse, HttpResponseRedirect
from django.http.response import HttpResponse
from django.shortcuts import render, get_object_or_404, redirect
from django.views import View
from django.views.generic import ListView
from django_filters.views import FilterView
from pages.filter import CeidFilter, PmRunDateFilter
from tools.forms import EntityForm, PMForm, CEIDForm, PDLAddForm
from tools.models import CEID, Entity, F4Events, ToolPm, WaferHandlingType, PmRunDate
from django.urls import reverse
class UserCeidPMUpdate(View):
template_name = 'tools/update_pm_attributes.html'
def get(self, request, username, pk, pm_id):
pm = ToolPm.objects.get(pk=pm_id)
form = PMForm(request.GET or None, instance=pm)
return render(request, self.template_name, {'form': form, 'pm': pm, })
def post(self, request, username, pk, pm_id):
pm = ToolPm.objects.get(pk=pm_id)
pm.user_edited = True
pm.save()
form = PMForm(request.POST or None, instance=pm)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('pm_view', kwargs={'username': username, 'pk': pm.ceid.id}))
return render(request, self.template_name, {'form': form, 'pm': pm, })
lastly, my .html file is:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form method="post">
<p>Process: {{ pm.ceid.process }}</p>
<p>CEID: {{ pm.ceid.ceid }}</p>
<p>Name: {{ pm.name }}</p>
<p>Frequency: {{ pm.frequency }}{% if pm.wafer_based %} wafers {% else %} days {% endif %}</p>
<p>Score: {{ pm.score }}</p>
{% csrf_token %}
{{ form }}
<input type="submit" class="btn btn-success" value="Update PM"><button type="button" class="btn btn-primary">Go Back</button>
</form>
{% endblock content %}
If I switch the pm_f4_events widget to a CheckboxSelectMultiple or a ModelSelect2Widget, the code works.
However, when I try to use a ModelSelect2MultipleWidget, the form continually tries to load but cannot and eventually just times out.
There are ~5000 items within the F4Events model, so that may have something to do with it. Any help to point me in the right direction would be greatly appreciated!
You can empty the pm_f4_events field's choices in the __init__ method:
class PMForm(forms.ModelForm):
class Meta:
model = ToolPm
fields = ['wafer_handling', 'wafer_based', 'handling_type', 'pm_f4_events']
widgets = {
'handling_type': forms.CheckboxSelectMultiple,
'pm_f4_events': s2forms.ModelSelect2MultipleWidget(
model=F4Events,
search_fields=["name__icontains",],
attrs = {'data-placeholder': 'Please Choose F4 Events'}
)
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
choices_F4Events = []
pm_f4_events_field = self.fields["pm_f4_events"]
# selected values
selected_F4Events = self.data.getlist("pm_f4_events") or self.initial.get("pm_f4_events")
if selected_F4Events:
# check of selected values not object
if not isinstance(selected_F4Events[0], F4Events):
selected_F4Events = pm_f4_events_field.queryset.filter(pk__in=selected_F4Events)
choices_F4Events = [(item.pk, item.name) for item in selected_F4Events]
# assign selected values or empty list
pm_f4_events_field.choices = choices_F4Events
Has been tested on my server, works well.
models.py
class MyVideo(models.Model):
title = models.CharField(max_length=100, null=True, default='')
seotitle = models.CharField(max_length=100, null=True, default='')
keywords = models.CharField(max_length=150, null=True, default='')
status = models.IntegerField(default=1)
serializers.py
class MyVideoSerializer(serializers.ModelSerializer):
class Meta:
model = MyVideo
fields = '__all__'
views.py
class My(viewsets.ModelViewSet):
queryset = MyVideo.objects.all()
serializer_class = MyVideoSerializer
renderer_classes = (JSONRenderer, TemplateHTMLRenderer,)
template_name = "my.html"
def get(self, request, *args, **kwargs):
# ??????
def get_query(self):
# ??????
urls.py
urlpatterns = [
path('my/', views.My), # ???????? anything wrong here?
]
my.html
<html><body>
<h1>My video</h1>
<ul>
{% for d in data %}
<li>{{ d.title }}</li> # ?????? anything wrong here?
<li>{{ d.seotitle }}</li>
<li>{{ d.keywords }}</li>
{% endfor %}
</ul>
</body></html>
I have a MyVideo model which store several videos record in the database. What I wanna implement is that to display the information of those videos through the my.html.
e.g. http://127.0.0.1:8000/my/103 can access the video which id=103, and on this page display its fields (title, seotitle, keywords, etc.).
Any nice implementation or suggestion? Thanks!
**UPDATE
<html><body>
<h1>My video</h1>
<ul>
{{ data }}
</ul>
</body></html>
from django.shortcuts import render
class My(viewsets.ModelViewSet):
queryset = MyVideo.objects.all()
serializeddata = MyVideoSerializer(queryset,many=true)
renderer_classes = (JSONRenderer, TemplateHTMLRenderer,)
template_name = "my.html"
def get(self, request, *args, **kwargs):
return render_to_response(template_name, {'data': serializeddata.data})
#or use below
#return render(
#request,
#template_name=template_name,
#{'data': serializeddata}
#)
def get_query(self):
# ??????
UPDATE
models.py
"""
Definition of models.
"""
from django.db import models
# Create your models here.
class MyVideo(models.Model):
title = models.CharField(max_length=100, null=True, default='')
seotitle = models.CharField(max_length=100, null=True, default='')
keywords = models.CharField(max_length=150, null=True, default='')
status = models.IntegerField(default=1)
serializers.py
from rest_framework import serializers
from app.models import MyVideo
class MyVideoSerializer(serializers.ModelSerializer):
class Meta:
model = MyVideo
fields = '__all__'
my.html
<html><body>
<h1>My video</h1>
<ul>
{% for d in data %}
<li>Title: {{ d.title }}</li>
<li>SEO Title:{{ d.seotitle }}</li>
<li>KeyWords {{ d.keywords }}</li>
{% endfor %}
</ul>
</body></html>
views.py
def my(request):
"""Renders the contact page."""
assert isinstance(request, HttpRequest)
queryset = MyVideo.objects.all()
serializer_class = MyVideoSerializer(queryset,many=True)
#datan = {"title":"Test Title"}
return render(
request,
'app/my.html',
{
'data':serializer_class.data,
}
)
NOTE: You can add if(request.method == GET): elif(request.method == POST)
OUTPUT:
I have my models.py
class Schedule(models.Model):
name = models.CharField(max_length=255)
date_from = models.DateField('')
date_to = models.DateField('', null=True)
desc = models.TextField(blank=True, null=True)
here my views.py
class Schedule(CreateView):
fields = ()
model = models.Schedule
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.save()
return super(ModelFormMixin, self).form_valid(form)
and here my template.html
{{form.as_p}}
this form only can do 1 time input. however I need to perform 3 times input in single form with different name & date (in my case).
and form maybe look like
{{form.as_p}}
{{form.as_p}}
{{form.as_p}}
I check on documentation theres bulk_create can do multiple input in single run but i dont have idea how to deal with my template.html
A demo for you:
views.py
from django import forms
from django.shortcuts import render, HttpResponse
from .models import Schedule
class ScheduleForm(forms.ModelForm):
class Meta:
model = Schedule
fields = "__all__"
def multicreate(request):
if request.method == "POST":
forms = [
ScheduleForm(dict(name=n, date_from=df, date_to=dt, desc=ds))
for n, df, dt, ds in zip(
request.POST.getlist("name"),
request.POST.getlist("date_from"),
request.POST.getlist("date_to"),
request.POST.getlist("desc"),
)
]
if all(forms[i].is_valid() for i in range(len(forms))):
for form in forms:
form.save()
return HttpResponse(
f"success to create {len(forms)} Schedule instances."
)
else:
forms = [ScheduleForm() for _ in range(3)]
return render(request, "create.html", {"forms": forms})
models.py
from datetime import date
from django.db import models
class Schedule(models.Model):
name = models.CharField(max_length=255)
date_from = models.DateField("date from", default=date.today)
date_to = models.DateField("date to", default=date.today)
desc = models.TextField(blank=True, null=True)
def __str__(self):
return self.name or self.__class__.__name__
template
<form method='post'>{% csrf_token %}
{% for form in forms %}
{{ form.Meta.model }} {{ forloop.counter }}<br>
{{ form.as_p }}
-------------------------------------------<br>
{% endfor %}
<input type='submit', value='OK'>
</form>