I'm playing with DRF and made a simple blog where anonymous people can comment on a blog post. I'm just using the browsable API at the moment, and everything seems to work fine until I try to post a comment. DELETE, GET, and PUT all work as expected, only POST.
The error I get is IntegrityError at /api/posts/i-had-a-blog-his-name-was-bingo/comments/: blog_comment.blogpost_id may not be NULL
I've searched thoroughly for an answer as to why this might be happening, but nothing is helping. Here's my code...
models.py
class BlogPost(models.Model):
created = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey('auth.User', related_name='posts')
title = models.CharField(max_length=100, unique=True)
content = models.TextField()
slug = models.SlugField(max_length=100, unique=True, editable=False)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(BlogPost, self).save(*args, **kwargs)
#permalink
def get_absolute_url(self):
return ('post-detail', { 'slug': self.slug })
class Meta:
ordering = ('created',)
class Comment(models.Model):
created = models.DateTimeField(auto_now_add=True)
blogpost = models.ForeignKey(BlogPost, related_name='comments')
author = models.CharField(max_length=100, blank=False)
content = models.TextField()
class Meta:
ordering = ('created', 'author', 'content')
serializers.py
class CommentSerializer(serializers.HyperlinkedModelSerializer):
post = serializers.Field(source='blogpost.title')
class Meta:
model = Comment
fields = ('id', 'author', 'content', 'post')
class BlogPostSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.Field(source='owner.username')
url = serializers.HyperlinkedIdentityField(view_name='post-detail')
comments = serializers.HyperlinkedIdentityField(view_name='comment-list')
class Meta:
model = BlogPost
fields = ('url', 'id', 'title', 'content', 'owner', 'comments')
views.py
class CommentList(generics.ListCreateAPIView):
serializer_class = CommentSerializer
def get_queryset(self):
slug = self.kwargs['slug']
return Comment.objects.filter(blogpost__slug=slug)
class CommentDetail(generics.RetrieveUpdateDestroyAPIView):
serializer_class = CommentSerializer
permission_classes = (IsAdminOrNoEdit,)
def get_queryset(self):
slug = self.kwargs['slug']
return Comment.objects.filter(blogpost__slug=slug)
urls.py
commentpatterns = patterns('',
url(r'^$', views.CommentList.as_view(), name='comment-list'),
url(r'^(?P<pk>[0-9]+)/$', views.CommentDetail.as_view(), name='comment-detail'),
)
urlpatterns = patterns('blog.views',
url(r'^$', 'api_root'),
url(r'^posts/$', views.PostList.as_view(), name='post-list'),
url(r'^posts/(?P<slug>[-\w]+)/$', views.PostDetail.as_view(), name='post-detail'),
url(r'^posts/(?P<slug>[-\w]+)/comments/', include(commentpatterns)),
url(r'^users/$', views.UserList.as_view(), name='user-list'),
url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view(), name='user-detail'),
)
Any help would be greatly appreciated, this is driving me crazy.
Your Comment model defines a ForeignKey, which is not allowed to be null:
class Comment(models.Model):
...
blogpost = models.ForeignKey(BlogPost, related_name='comments')
...
which is ok, but your serializer does not include the blogpost id, so even if your request includes it, it will be just ignored. correct your serializer to include the blogpost field:
class CommentSerializer(serializers.HyperlinkedModelSerializer):
post = serializers.Field(source='blogpost.title')
blogpost = serializers.PrimaryKeyRelatedField()
class Meta:
model = Comment
fields = ('id', 'author', 'content', 'post', 'blogpost')
now when you create a post request, the blogpost field should contain the id of the blog post to which you're attaching this comment.
Related
I got the these two errors below:
<class 'blog.admin.CommentAdmin'>: (admin.E108) The value of
'list_display[4]' refers to 'active', which is not a callable, an
attribute of 'CommentAdmin', or an attribute or method on
'blog.Comment'.
<class 'blog.admin.CommentAdmin'>: (admin.E116) The value of
'list_filter[0]' refers to 'active', which does not refer to a Field.
This is my models.py code:
from django.contrib.auth.models import User
# Create your models here.
STATUS = (
(0,"Draft"),
(1,"Publish")
)
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete= models.CASCADE,related_name='blog_posts')
updatedOn = models.DateTimeField(auto_now= True)
content = models.TextField()
createdOn = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
class Meta:
ordering = ['-createdOn']
def __str__(self):
return self.title
class Comment(models.Model):
post = models.ForeignKey(
Post, on_delete=models.CASCADE, related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = models.TextField()
createdOn = models.DateTimeField(auto_now_add=True)
status = models.BooleanField(default=False)
class Meta:
ordering = ['createdOn']
def __str__(self):
return 'Comment {} by {}'.format(self.body, self.name)
This is my admin.py code:
from django.contrib import admin
from .models import Post, Comment
# Register your models here.
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'slug', 'status','createdOn')
list_filter = ("status", 'createdOn')
search_fields = ['title', 'content']
prepopulated_fields = {'slug': ('title',)}
#admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display = ('name', 'body', 'post', 'createdOn', 'active')
list_filter = ('active', 'createdOn')
search_fields = ('name', 'email', 'body')
actions = ['approveComments']
def approveComments(self, request, queryset):
queryset.update(active=True)
admin.site.register(Post, PostAdmin)
This is my forms.py code:
from .models import Comment
from django import forms
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'email', 'body')
Any help is greatly appreciated.
The message is clear 'active' is not a field
class Comment(models.Model):
post = models.ForeignKey(
Post, on_delete=models.CASCADE, related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = models.TextField()
createdOn = models.DateTimeField(auto_now_add=True)
status = models.BooleanField(default=False)
your fields are: post, name, email, createdOn, status
Therefore create a field named active or suppress active in list_display &
list_filter
status = models.IntegerField(choices=STATUS, default=0)
should be:
active = models.IntegerField(choices=STATUS, default=0)
I got the same error below:
ERRORS: <class 'store.admin.PersonAdmin'>: (admin.E116) The value of
'list_filter[0]' refers to 'PersonAgeFilter', which does not refer to
a Field.
When assigning the custom filter PersonAgeFilter to list_filter() with parentheses as shown below:
# "store/admin.py"
from django.contrib import admin
from .models import Person
class PersonAgeFilter(admin.SimpleListFilter):
# ...
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
list_filter = ("PersonAgeFilter",)
# ↑ Parentheses ↑
So, I removed the parentheses from "PersonAgeFilter" as shown below then the error was solved.
# "store/admin.py"
from django.contrib import admin
from .models import Person
class PersonAgeFilter(admin.SimpleListFilter):
# ...
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
list_filter = (PersonAgeFilter,)
# Without parentheses
I have the following django rest framework serializer and view for a model Post, and the models defined for the app are as following.
Now I wanted to test the API, so tried to "create" a new post from the API page, but then I got an error IntegrityError at /api/posts/ NOT NULL constraint failed: appname_post.user_id.
So then I tried to debug and checked its request.data value, it says it only has title and content but not user_id, as I could expect from the error above.
But now I have no idea how to tell the API automatically relate the user_id field to request.user.id. Isn't not enough to just add the user_id = serializers.ReadOnlyField(source='user.id') line? I could do "retrieve", "update", "patch" etc but just cannot "post" (create).
Thanks for your help.
serializer and view
class DevPerm(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return True
def has_permission(self, request, view):
return True
class PostSerializer(serializers.HyperlinkedModelSerializer):
user_id = serializers.ReadOnlyField(source='user.id')
class Meta:
model = Post
fields = ('url',
'id',
'title',
'content',
'created_at',
'voters',
'comments',
'user_id',
)
read_only_fields = (
'id',
'created_at',
"voters",
"comments",
# "user_id",
)
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [permissions.IsAuthenticated, DevPerm,]
models
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts', default="")
created_at = models.DateTimeField(auto_now_add=True)
title = models.TextField(blank=True)
content = models.TextField(blank=True)
voters = models.ManyToManyField(User, related_name='votes', default="")
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comments', default="")
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments', default="")
created_at = models.DateTimeField(auto_now_add=True)
content = models.TextField(blank=True)
You can override perform_create method inside you ModelViewSet.
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [permissions.IsAuthenticated, DevPerm,]
def perform_create(self, serializer):
serializer.save(user=self.request.user)
I tried to add page for posts blog article for my django site.But it's slug model is not autogenerate after add it into the add post page but it work well in admin page.
example in title field when i type how to master python fastly it will auto generated in slug field with "-" in only admin page.but when I type same thing on add post page it won't generate slug automatically.
mycode
models.py
from django.db import models
from django.contrib.auth.models import User
from django_summernote.fields import SummernoteTextField
from django.urls import reverse
from django.template.defaultfilters import slugify
STATUS = (
(0,"Draft"),
(1,"Publish")
)
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
title_type = models.CharField(max_length=50, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete= models.CASCADE,related_name='blog_posts')
updated_on = models.DateTimeField(auto_now= True)
content = SummernoteTextField(blank=True, null=True)
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
image = models.ImageField(upload_to='images',null=True, blank=True)
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('home')
views.py
class AddPostView(CreateView):
model = Post
form_class = PostForm
template_name = 'add_post.html'
admin page
And add post page
The slug field should not be editable, remove the 'slug' field from the fields of AddPostView and of the Admin.
class AddPostView(CreateView):
model = Post
fields = ['title', 'title_type', 'author', 'updated_on', 'content',
'created_on', 'status', 'image']
form_class = PostForm
template_name = 'add_post.html'
I have a page where a blog post detail is displayed. Under the post, there is a section where user can comment after inputing thier name subject and text in a comment box. Now i have to make an api for this. I want to make a post api such that that comment is stored/associated to that particular blogpost detail. That means i need blogpost id to pass while posting comment. How to do that??
class BlogPost(models.Model):
CATEGORY_CHOICES = (
('travel_news', 'Travel News',),
('travel_tips', 'Travel Tips',),
('things_to_do', 'Things to Do',),
('places_to_go', 'Places to Go'),
)
image = models.ImageField(blank=True, null=True)
categories = models.CharField(max_length=64, choices=CATEGORY_CHOICES, default='travel_news')
description = models.CharField(max_length=255)
content = RichTextUploadingField()
# todo support for tags
tags = models.CharField(max_length=255, default='#travel') #todo
date_created = models.DateField()
#property
def html_stripped(self):
from django.utils.html import strip_tags
return strip_tags(self.content)
#property
def comments(self):
return self.comments_set.all()
Here are my serializers:
class CommentPostSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
# fields = '__all__'
fields = ['name', 'email', 'subject', 'comment',]
class BlogPostSerializer(serializers.ModelSerializer):
comments = CommentListSerializer(many=True)
class Meta:
model = BlogPost
fields = ['image', 'categories', 'description', 'content', 'tags', 'date_created', 'comments']
# fields = '__all__'
Here is my view:
class CommentCreateAPIView(CreateAPIView):
queryset = Comment.objects.all()
serializer_class = CommentPostSerializer
I want to add a dropdown with autocomplete filter such as select2 into django admin form with class based model. i have tried several tricks avilable over the internet but not succeeded. here are some code snippet i have. i want to show all category for a post which is already available into model.
in my model.py
class Post(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
featured_image = models.ImageField(null=True, blank=True, upload_to="blog/", verbose_name='Featured Image')
author = models.ForeignKey(User, on_delete= models.CASCADE,related_name='blog_posts')
updated_on = models.DateTimeField(auto_now= True)
content = RichTextUploadingField()
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
class Meta:
ordering = ['-created_on', 'title']
def __str__(self):
return self.title
def _generate_slug(self):
value = self.title
slug_candidate = slugify(value, allow_unicode=True)
self.slug = slug_candidate
def save(self, *args, **kwargs):
if not self.pk:
self._generate_slug()
super().save(*args, **kwargs)
my admin.py
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'slug', 'status', 'category', 'author','created_on')
list_filter = ("status",)
search_fields = ['title', 'content']
prepopulated_fields = {'slug': ('title',)}
actions = [export_as_csv_action("CSV Export", fields=['title','slug','author','featured_image','status','created_on','updated_on'])]
how my form looks into django-admin
please suggest anything how to add i filter for category dropdown filter with autocomplete.
In Django 2.0+ you can just add autocomplete_fields to the ModelAdmin:
class PostAdmin(admin.ModelAdmin):
autocomplete_fields = ['category']
This will add asynchronous search of all categories the user has access to (versus a standard dropdown/select).