I am trying to make a nested comment functionality using django rest framework.
I am using Django 2.2 and rest framework version 3.10.2 but here i am following a video in youtube that uses Django 1.9.
No matter what value i passed in URL, it always return a validation error stating that
model_qs is empty.
I am not able to solve this issue. Can someone please look into it and
let me point out what i am doing wrong here.
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
urls.py
urlpatterns = [
path('create/', CommentCreateAPIView.as_view(), name='create'),
]
model.py
class CommentManager(models.Manager):
def all(self):
qs = super(CommentManager, self).filter(parent=None)
return qs
def filter_by_instance(self, instance):
content_type = ContentType.objects.get_for_model(instance.__class__)
obj_id = instance.id
qs = super(CommentManager, self).filter(content_type=content_type, object_id=obj_id).filter(parent=None)
return qs
def create_by_model_type(self, model_type, slug, content, user, parent_obj=None):
model_qs = ContentType.objects.filter(model=model_type)
if model_qs.exists():
some_model = model_qs.first().model_class()
obj_qs = some_model.objects.filter(slug=slug)
if obj_qs.exists() and obj_qs.count() == 1:
instance = self.model()
instance.content = content
instance.user = user
instance.content_type = model_qs.first()
instance.object_id = obj_qs.first().id
if parent_obj:
instance.parent = parent_obj
instance.save()
return instance
return None
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
content = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
objects = CommentManager()
class Meta:
ordering = ['-timestamp']
def __unicode__(self):
return str(self.user.username)
def __str__(self):
return str(self.user.username)
def get_absolute_url(self):
return reverse("comments:thread", kwargs={"id": self.id})
def get_delete_url(self):
return reverse("comments:delete", kwargs={"id": self.id})
def children(self): # Replies
return Comment.objects.filter(parent=self)
#property
def is_parent(self):
if self.parent is not None:
return False
return True
serializers.py
def create_comment_serializer(model_type='post', slug=None, parent_id=None, user=None):
class CommentCreateSerializer(ModelSerializer):
class Meta:
model = Comment
fields = [
'id',
'parent',
'content',
'timestamp',
]
def __init__(self, *args, **kwargs):
self.model_type = model_type
self.slug = slug
self.parent_obj = None
if parent_id:
parent_qs = Comment.objects.filter(id=parent_id)
if parent_qs.exists() and parent_qs.count() == 1:
self.parent_obj = parent_qs.first()
return super(CommentCreateSerializer, self).__init__(*args, **kwargs)
def validate(self, data):
model_type = self.model_type
model_qs = ContentType.objects.filter(model=model_type)
print(model_qs) # This always return <QuerySet []>
if not model_qs.exists() or model_qs.count() != 1:
raise ValidationError("This is not a valid content types")
some_model = model_qs.first().model_class()
obj_qs = some_model.objects.filter(slug=self.slug)
if not obj_qs.exists() or obj_qs.count() != 1:
raise ValidationError("This is not a valid slug")
return data
def create(self, validated_data):
content = validated_data.get("content")
if user:
main_user = user
else:
main_user = User.objects.all().first()
model_type = self.model_type
slug = self.slug
parent_obj = self.parent_obj
comment = Comment.objects.create_by_model_type(model_type, slug, content, main_user, parent_obj=parent_obj,)
return comment
return CommentCreateSerializer
views.py
class CommentCreateAPIView(CreateAPIView):
queryset = Comment.objects.all()
# permission_classes = [IsAuthenticated]
def get_serializer_class(self):
model_type = self.request.GET.get("type")
slug = self.request.GET.get("slug")
parent_id = self.request.GET.get("parent_id", None)
return create_comment_serializer(
model_type=model_type,
slug=slug,
parent_id=parent_id,
user=self.request.user
)
Related
While rendering the form in web browser, the dropdown menu of CPA_Clients should be empty since I've used "self.fields['name'].queryset = CPA_Client.objects.none()" (Since I'll be making it chained dropdown) in forms.py file, but I still see all the contents of CPA_Clients in dropdown.
Where did I go wrong?
forms.py
from django import forms
from .models import Task, CPA_Client
class TaskDetails(forms.Form):
class Meta:
model = Task
fields = '__all__'
def __init__(self, *args, **kwargs):
super(TaskDetails, self).__init__(*args, **kwargs)
self.fields['name'].queryset = CPA_Client.objects.none()
models.py
from django.db import models
from django.contrib.auth.models import User
from datetime import datetime
from uuid import uuid4
from django.urls import reverse
import os
#Create your models here.
class TaskType(models.Model):
name = models.CharField(max_length=40)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'Task Type'
class CPAsList(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'CPAs'
class CPA_Client(models.Model):
CPA = models.ForeignKey(CPAsList,on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'CPA\'s Clients'
def get_random_filename(instance, filename):
now = datetime.now()
path = now.strftime("%Y/%B/%d")
instance.filename = filename
ext = filename.split('.')[-1]
filename = "%s.%s" % (str(uuid4()), ext)
return os.path.join(path, filename)
TASK_STAGE = [
('NE','New'),
('OG','Ongoing'),
('CM','Completed')
]
PRIORITY = [
('HI','High'),
('ME','Medium'),
('LO','Low')
]
class Document(models.Model):
file = models.FileField(upload_to=get_random_filename, default="")
filename = models.CharField(max_length=128, editable=False, default="")
def __str__(self):
return self.filename
class Task(models.Model):
Stage = models.CharField(max_length=100, choices=TASK_STAGE, default='NE')
Subject_Line = models.CharField(max_length=255, default="", editable=True,)
Task_Type = models.ForeignKey(TaskType, on_delete=models.SET_NULL, null=True)
CPA = models.ForeignKey(CPAsList, on_delete=models.SET_NULL, null=True)
Client_of_CPA = models.ForeignKey(CPA_Client, on_delete=models.SET_NULL, null=True)
Priority = models.CharField(max_length=100, choices = PRIORITY, default='ME')
Assign_to = models.ForeignKey(User, on_delete=models.CASCADE, related_name='assignedUser')
Date_Added = models.DateTimeField(editable=True, null=True, default=datetime.now)
Additional_Note = models.TextField(blank=True, null=True)
Author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='assigneeUser', null=True, editable=False)
def __str__(self):
return self.Subject_Line
def get_absolute_url(self):
return reverse('task-detail', kwargs={'pk': self.pk})
class TimeStamps(models.Model):
Task_ID = models.ForeignKey(User, on_delete=models.CASCADE)
TimeStamp = models.IntegerField(default=0)
views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.http import JsonResponse, HttpResponse
from .models import Task
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from .models import Task, CPAsList, CPA_Client, TaskType, Document
from .forms import TaskDetails
from django.contrib.auth.decorators import login_required
# Create your views here.
#login_required
def home(request):
context = {
'tasks': Task.objects.all()
}
return render(request, 'crm/home.html', context)
class TaskListView(ListView):
model = Task
template_name = 'crm/home.html'
context_object_name = 'tasks'
ordering = ['-Date_Added']
class TaskDetailView(DetailView):
model = Task
class TaskDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Task
success_url = '/'
def test_func(self):
task = self.get_object()
if self.request.user == task.Author:
return True
return False
class TaskCreateView(LoginRequiredMixin, CreateView):
model = Task
fields = '__all__'
success_url = '/'
def model_form_upload(self, form):
if request.method == 'POST':
form = TaskDetails(request.POST, request.FILES)
if form.is_valid():
form.instance.Author = self.request.user
return super().model_form_upload(form)
else:
form = TaskDetails()
return render(request, 'crm-home', {'form': form})
class TaskUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Task
fields = '__all__'
#success_url = '/'
def model_form_upload(self, form):
if request.method == 'POST':
form = TaskDetails(request.POST, request.FILES)
if form.is_valid():
form.Author = self.request.user
return super().model_form_upload(form)
else:
form = TaskDetails()
return render(request, 'crm-home', {'form': form})
def test_func(self):
task = self.get_object()
if self.request.user == task.Author:
return True
return False
def contact(request):
return render(request, 'crm/contact.html')
def update_counter(request):
if request.method == 'POST':
counterValue = request.POST['counter']
startTime = request.POST['timestamp']
print(counterValue)
print(startTime)
message = 'update successful'
return HttpResponse(message)
#AJAX
def load_cities(request):
country_id = request.GET.get('country_id')
cities = City.objects.filter(country_id=country_id).all()
return render(request, 'persons/city_dropdown_list_options.html', {'cities': cities})
# return JsonResponse(list(cities.values('id', 'name')), safe=False)
Your Task model has no name field. The Client_of_CPA field is a ForeignKey to the CPA_Client model:
class TaskDetails(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['Client_of_CPA'].queryset = CPA_Client.objects.none()
class Meta:
model = Task
fields = '__all__'
Your TaskCreateView and TaskUpdateView do not use your form however, you need to specify this with:
class TaskCreateView(LoginRequiredMixin, CreateView):
model = Task
form_class = TaskDetails
success_url = '/'
template_name = 'crm-home'
def form_valid(self, form):
form.instance.Author = self.request.user
return super().form_valid(form)
class TaskUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Task
form_class = TaskDetails
success_url = '/'
template_name = 'crm-home'
def form_valid(self, form):
form.instance.Author = self.request.user
return super().form_valid(form)
def test_func(self):
task = self.get_object()
return self.request.user == task.Author
I got an attribute error on my NarrateUpdate view. I want to edit/update a user submission from a form
Here's my views.py:
class NarrateUpdate(SuccessMessageMixin, UpdateView):
model = Narrate
fields = ['title', 'body']
template_name = 'narrate_update_form.html'
success_url = reverse_lazy('narrate-status')
success_message = "Narration successfully updated"
def get(self, request, *args, **kwargs):
footer = FooterLinks.objects.all()
context = self.get_context_data(**kwargs)
context['footer'] = footer
return self.render_to_response(context)
The urls.py
path('narration/<int:pk>/update/', NarrateUpdate.as_view(), name='edit-narration'),
And here's the models.py:
class Narrate(models.Model):
STATUS = (
('F', 'FOR REVIEW'),
('P', 'PASSED'),
)
title = models.CharField(max_length=255)
body = models.CharField(max_length=10000)
status = models.CharField(
max_length=25, choices=STATUS, default='for_review', blank=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return "/narration/%i/" % self.title
class Meta:
verbose_name = "narration"
verbose_name_plural = "narrations"
What can I do to make this work?
Why are you overriding the get method? If you want to add footer better to do it in get_context_data() method:
class NarrateUpdate(SuccessMessageMixin, UpdateView):
model = Narrate
fields = ['title', 'body']
template_name = 'narrate_update_form.html'
success_url = reverse_lazy('narrate-status')
success_message = "Narration successfully updated"
def get_context_data(self, **kwargs):
context = super(NarrateUpdate, self).get_context_data(**kwargs)
context['footer'] = FooterLinks.objects.all()
return context
I created an update screen for django. However, although it redirects to successURL, the data has not been updated. I don't know why.
I need your help.
I will post it if necessary.
#view
class RecordDetailEdit(UpdateView,LoginRequiredMixin):
template_name = 'records/detail_edit.html'
model = URC
form_class = RecordDetailEditForm
pk_url_kwarg = 'id'
success_url = reverse_lazy('person:home')
def get_object(self):
return get_object_or_404(User, pk=self.request.user.user_id)
def get_form_kwargs(self):
kwargs = super(RecordDetailEdit, self).get_form_kwargs()
# get users, note: you can access request using: self.request
kwargs['user'] = self.request.user
return kwargs
#form
class RecordDetailEditForm(forms.ModelForm):
class Meta:
model = URC
fields = ('UPRC','URN','UET','URT')
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(RecordDetailEditForm, self).__init__(*args, **kwargs)
self.fields['URN'].queryset = UPRM.objects.filter(user=user)
#model
class URC(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
UPRC = models.CharField(max_length=300)
URN = models.ForeignKey(UPRM, on_delete=models.CASCADE)
UET = models.DurationField(editable=True)
URT = models.DateTimeField(default=timezone.now,editable=True)
group = models.ForeignKey(group, on_delete=models.CASCADE, null=True)
def __str__(self):
return self.UPRC
#url
path('<id>/edit/', views.RecordDetailEdit.as_view(), name='record_detail_edit'),
I changed it to the following.
def get_object(self, queryset=None):
obj = URC.objects.get(id=self.kwargs['id'])
return obj
I am not good at English.
Please understand me.
I'm creating a blog in Django.
I am making it into class view, but I want to see a reple list and form in the post details view.
like this
enter image description here
POST detailview has already been implemented and I don't know how to add comments.
Should I be function view?
Give me some advice.
code
views.py
class BlogLV(LoginRequiredMixin,ListView):
model = Blog
def get_queryset(self):
return Blog.objects.exclude(user=self.request.user)
class BlogCV(LoginRequiredMixin,CreateView): # blog_form.html
model = Blog
fields = ['name','description','image']
success_url = reverse_lazy('blog:index')
def form_valid(self, form): #오류
form.instance.user = self.request.user #user 설정
form.instance.slug = self.request.user
return super(BlogCV,self).form_valid(form)
class BlogDV(LoginRequiredMixin,DetailView):
model = Blog
class PostCreateView(LoginRequiredMixin,CreateView):
model = Post
fields = ['title', 'content']
success_url = reverse_lazy('blog:index')
template_name = 'blog/post_form.html'
def get_context_data(self, **kwargs):
context = super(PostCreateView, self).get_context_data(**kwargs)
context['blog'] = Blog.objects.get(slug=self.kwargs['slug'])
return context
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.slug = self.request.user
return super(PostCreateView, self).form_valid(form)
class PostDV(LoginRequiredMixin,DetailView):
model = Post
fields =['title','content']
def get_context_data(self, **kwargs):
context = super(PostDV, self).get_context_data(**kwargs)
context['blog'] = Blog.objects.get(slug=self.kwargs['slug'])
return context
class PostLV(LoginRequiredMixin,ListView):
model = Post
paginate_by = 5
def get_context_data(self, *, object_list=None, **kwargs):
context = super(PostLV,self).get_context_data(**kwargs)
context['blog'] = Blog.objects.get(slug=self.kwargs['slug'])
return context
def get_queryset(self):
return Post.objects.filter(slug=self.kwargs['slug'])
class PostDeleteView(LoginRequiredMixin,DeleteView):
model = Post
success_url = reverse_lazy('blog:index')
def get_context_data(self, *, object_list=None, **kwargs):
context = super(PostDeleteView,self).get_context_data(**kwargs)
context['blog'] = Blog.objects.get(slug=self.kwargs['slug'])
return context
class PostUV(LoginRequiredMixin,UpdateView):
model = Post
fields = ['title','content']
success_url = reverse_lazy('blog:index')
def get_context_data(self, *, object_list=None, **kwargs):
context = super(PostUV,self).get_context_data(**kwargs)
context['blog'] = Blog.objects.get(slug=self.kwargs['slug'])
return context
urls.py
urlpatterns = [
url(r'^$',BlogLV.as_view(),name="index"),
url(r'^create/$',BlogCV.as_view(),name='blog_create'),
url(r'^(?P<slug>[-\w]+)/$',BlogDV.as_view(),name='blog_detail'),
url(r'^(?P<slug>[-\w]+)/post/create/$',PostCreateView.as_view(),name='post_create'),
url(r'^(?P<slug>[-\w]+)/post/$',PostLV.as_view(),name='post_list'),
url(r'^(?P<slug>[-\w]+)/post/(?P<pk>[0-9]+)/$', PostDV.as_view(), name='post_detail'),
url(r'^(?P<slug>[-\w]+)/update/(?P<pk>[0-9]+)/$', PostUV.as_view(), name='post_update'),
url(r'^(?P<slug>[-\w]+)/delete/(?P<pk>[0-9]+)/$', PostDeleteView.as_view(), name='post_delete'),
]
models.py
#python_2_unicode_compatible
class Blog(models.Model): # 유저 당 하나
name = models.CharField(max_length=20)
description = models.CharField(max_length=30)
image = models.ImageField(upload_to='blog/profile')
create_date = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(unique=True,allow_unicode=True)
# 블로그 생성한 시각
# 저장 경로: MEDIA_ROOT/blog/projile/xxx.jpg 파일 저장
# DB 필드 'MEDIA_URL/blog/profile/xxx.jpg' 문자열 저장
user = models.OneToOneField(User)
class Meta:
ordering = ['-create_date']
# 생성된 날짜의 내림차순으로 정렬
def __str__(self):
return self.name
#python_2_unicode_compatible
class Post(models.Model):
title = models.CharField(max_length=30)
content = models.TextField()
create_date = models.DateTimeField(auto_now_add=True)
modify_date = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User)
slug = models.SlugField(allow_unicode=True,default='slug')
class Meta :
ordering = ['-create_date']
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:post_detail',args=(self.id,))
# post.get_absolute_url args
# 객체가 지칭하는 url 반환
def get_previous_post(self):
return self.get_previous_by_create_date()
#create_date 기준으로 이전 포스트 반환
# get_previous_by_column 내장객체 호출
def get_next_post(self):
return self.get_next_by_create_date()
#python_2_unicode_compatible
class Reple(models.Model):
content = models.TextField()
user = models.ForeignKey(User)
Post = models.IntegerField() # post_id
create_date = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-create_date']
def __str__(self):
return self.content
It's my first time writing, so I don't know how to Indent. Sorry
And if my code is not effective, tell me how to fix it.
thank you!
It might be easier to write the view from scratch as a function, but you can also override the existing post method to return additional data.
After extending an existing user model, I get RelatedObjectDoesNotExist exception with a value User has no dcf_profile. I seems that dcf_profile isn't created automatically for each user.
Please take a look at my model, view and form below and tell me how can I correct my views file?
My models.py :
class CustomUser(models.Model):
auth_user_ptr = models.OneToOneField(
User,
parent_link=True,
related_name='dcf_profile',
primary_key=True
)
phone = models.CharField(_('phone'), max_length=30, null=True, blank=True)
receive_news = models.BooleanField(_('receive news'), default=True, db_index=True)
class Meta:
app_label = 'dcf'
def allow_add_item(self):
if self.item_set.count() > settings.DCF_ITEM_PER_USER_LIMIT:
return False
else:
return True
class Item(models.Model):
slug = models.SlugField(blank=True, null=True, max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE)
group = models.ForeignKey(Group, verbose_name=_('group'))
title = models.CharField(_('title'), max_length=100)
description = models.TextField(_('description'))
price = models.DecimalField(_('price'), max_digits=10, decimal_places=2)
phone = models.CharField(_('phone'), max_length=30)
is_active = models.BooleanField(_('display'), default=True, db_index=True)
updated = models.DateTimeField(_('updated'), auto_now=True, db_index=True)
posted = models.DateTimeField(_('posted'), auto_now_add=True)
def __unicode__(self):
return self.title
class Meta:
verbose_name = _('item')
verbose_name_plural = _('items')
ordering = ('-updated', )
def get_absolute_url(self):
return reverse('item', kwargs={
'pk': self.pk,
'slug': self.slug
})
def get_title(self):
return u'%s' % self.title
def get_description(self):
return u'%s' % self.description[:155]
def get_keywords(self):
# TODO need more optimal keywords selection
return ",".join(set(self.description.split()))
def get_related(self):
# TODO Need more complicated related select
return Item.objects.exclude(pk=self.pk)[:settings.DCF_RELATED_LIMIT]
def save(self, *args, **kwargs):
if self.slug is None:
self.slug = slugify(unidecode(self.title))
super(Item, self).save(*args, **kwargs)
My views.py :
class ItemCreateView(FormsetMixin, CreateView):
is_update_view = False
model = Item
form_class = ItemCreateEditForm
formset_class = inlineformset_factory(Item, Image, extra=3, fields=('file', ))
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
if not self.request.user.dcf_profile.allow_add_item():
messages.error(self.request, _('You have reached the limit!'))
return redirect(reverse('my'))
return super(ItemCreateView, self).dispatch(*args, **kwargs)
def form_valid(self, form, formset):
form.instance.user = self.request.user
form.save()
return super(ItemCreateView, self).form_valid(form, formset)
My forms.py
class ItemCreateEditForm(forms.ModelForm):
class Meta:
model = Item
fields = ('group', 'title', 'description', 'price', 'phone', 'is_active')
Just to share with you a solution that worked, I have created a proxy model of the User model and updated my views.py :
Updated models.py
class DcfUser(User):
class Meta:
proxy = True
def allow_add_item(self):
if self.item_set.count() > settings.DCF_ITEM_PER_USER_LIMIT:
return False
else:
return True
Updated views.py
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
user = self.request.user
my_user = DcfUser.objects.get(username=user)
if not my_user.allow_add_item():
messages.error(self.request, _('You have reached the limit!'))
return redirect(reverse('my'))
Thank you