I am getting Integrity error in django rest framework - django

I am begginer in django. I would like to add some posts and comments but I am getting an Integrity error.
Without comments model it was working before but it doesn´t work together. I already delete my database and makemigrations and migrate again.
post models
from django.db import models
from django.conf import settings
# from django.contrib.auth import get_user_model
# User = get_user_model()
# Create your models here.
class Post(models.Model):
user = models.ForeignKey(
#to=User,
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='posts',
null=True
)
content = models.CharField(
max_length=150,
blank=False
)
created = models.DateTimeField(
auto_now=True
)
liked_by = models.ManyToManyField(
#to=User,
to=settings.AUTH_USER_MODEL,
related_name='liked_posts',
blank=True
)
post serializer
from rest_framework import serializers
from .models import Post
from ..comment.serializers import CommentSerializer
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
comments = CommentSerializer(source='comments.content')
fields = [
'id',
'user',
'content',
'comments',
'created',
'liked_by',
]
comment.models
from django.db import models
from django.conf import settings
from apps.post.models import Post
# Create your models here.
class Comment(models.Model):
user = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='comment', null=True)
post = models.ForeignKey(to=Post, on_delete=models.SET_NULL, related_name='comment', null=True)
content = models.CharField(max_length=150)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'Comment by: {self.user}'
comment serializer
from rest_framework import serializers
from .models import Comment
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ['id', 'user', 'post', 'content', 'created']

you need to pass the CommentSerializer field in PostSerializer Meta class properly.
from rest_framework import serializers
from .models import Post
from .comment.serializers import CommentSerializer
class PostSerializer(serializers.ModelSerializer):
comments = CommentSerializer(many=True)
class Meta:
model = Post
fields = [
'id',
'user',
'content',
'comments',
'created',
'liked_by',
'comments',
]

Related

Could not resolve URL for hyperlinked relationship using view name "user-detail"

Could not resolve URL for hyperlinked relationship using view name "user-detail". You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field.
Serializer.py
from .models import Post
from rest_framework import serializers
class PostSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='user-detail',
source='profile',)
class Meta:
model = Post
fields = ['url',
'title',
'slug',
'author',
'updated_on',
'content',
'created_on',
'status']
Models.py
from django.db import models
from django.contrib.auth.models import User
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')
updated_on = models.DateTimeField(auto_now=True)
content = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
blog/urls.py
from rest_framework import routers
from django.urls import path, include
from . import views
router = routers.DefaultRouter()
router.register(r'post', views.PostList)
urlpatterns = [
path('api/', include(router.urls)),
path('api-auth/', include('rest_framework.urls')),
path('', views.PostList.as_view({'get': 'list'}), name='home'),
path('<slug:slug>/', views.PostDetail.as_view(), name='post_detail'),
]
Project structure:
enter image description here
I've been sitting on this problem for a long time, I can not solve it in any way. There are already several questions on stackowerflow with this error but none helped me
This is happening because you don't have a viewset/url for the user model.
Option 1:
You can create a new viewset for the user model and register it with the router.
Option 2:
create and directly use the Userserializer on the PostSerializer like this
class UserSerializer(serializers.ModelSerializer):
class Meta:
# import the user model
model = User
fields = ("id",)
class PostSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='user-detail',
source='profile',)
author = UserSerializer()
class Meta:
model = Post
fields = ['url', 'title', 'slug',
'author', 'updated_on', 'content',
'created_on', 'status']
Read more here: https://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects

Extended the User model, but not quite sure how to serialize fields for both User and UserExtended

I extended my User model with a new model just called UserExtended:
# Django imports
from django.db import models
from django.contrib.auth.models import User
class UserExtended(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
crm_guid = models.UUIDField(unique=True)
security_q1 = models.CharField(max_length=255, blank=True, null=True)
security_a1 = models.CharField(max_length=255, blank=True, null=True)
security_q2 = models.CharField(max_length=255, blank=True, null=True)
security_a2 = models.CharField(max_length=255, blank=True, null=True)
attempts = models.SmallIntegerField(blank=False, null=False, default=0)
key = models.CharField(max_length=255, blank=True, null=True)
key_expires = models.DateTimeField(blank=True, null=True)
method = models.CharField(max_length=4, blank=True, null=True)
class Meta:
db_table = 'auth_user_extended'
I was hoping by just doing that some Django magic would take care of the rest and I wouldn't have to change my views.py or serializers.py. But when I send a request to the end-point I get:
[api] django.core.exceptions.ImproperlyConfigured: Field name `guid` is not valid for model `User`.
So it does apparently need to be specified. I've been looking at the documentation and similar SO questions to find an answer.
This is what I have for my views.py:
# Django imports
from django.contrib.auth.models import User
# Third party imports
from rest_framework import generics
from rest_framework.permissions import IsAdminUser
# App imports
from users.serializers import UserSerializer
class UsersListCreateView(generics.ListCreateAPIView):
permission_classes = [IsAdminUser]
serializer_class = UserSerializer
def get_queryset(self):
queryset = User.objects.all()
email = self.request.query_params.get('email')
username = self.request.query_params.get('username')
if email:
queryset = queryset.filter(email=email)
if username:
queryset = queryset.filter(username=username)
return queryset
class UserRetrieveUpdateDeleteView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsAdminUser]
queryset = User.objects.all()
serializer_class = UserSerializer
For my serializers.py I just have:
# Django imports
from django.contrib.auth.models import User
from users.models import UserExtended
from django.contrib.auth.hashers import make_password
# Third party imports
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'last_login', 'first_name',
'last_name', 'username', 'email', 'is_active', 'guid']
If I change model = User to model = UserExtemded, then I'll get an error like:
[api] django.core.exceptions.ImproperlyConfigured: Field name `last_login` is not valid for model `UserExtended`.
I'm thinking I need to do one of two things:
Create a serializer class for both models and call them both from the views.py. I've toyed with this a little by trying to pass a list or tuple in of serializer_class (apparently singular for a reason).
Setup the relationship in the serializers.py. I'm looking into this now.
Suggestions for how to resolve this issue?
You need a different serializer and viewset to operate on UserExtended
My suggestion would be keep old serializer as is and create UserExtendedSerializer
class UserExtendedSerializer(serializers.ModelSerializer):
user = UserSerializer(many=False, read_only=True)
class Meta:
model = UserExtended
fields = "__all__"
and viewset would be simply:
class UserExtendedViewSet(ModelViewSet):
serializer_class = UserExtendedSerializer
queryset = UserExtended.objects.all()
this should solve your issue

Django Rest Framework link logged user to app model

When I add a new Article, I have a dropdown with a list of all registered users. I want to link the current user to the new Article.
models.py
from django.db import models
from django.conf import settings
User = settings.AUTH_USER_MODEL
class Article(models.Model):
title = models.CharField(max_length=100)
user = models.OneToOneField(User, related_name='Article', on_delete=models.CASCADE)
def __str__(self):
return self.title
serializers.py
from rest_framework import serializers
from .models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ['id', 'title', 'user']
Here is the solution:
class ArticleSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(
read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = Article
fields = ['id', 'title', 'user']
def save(self, **kwargs):
kwargs["user"] = self.fields["user"].get_default()
return super().save(**kwargs)

Django Rest Framework: Serialize multiple images to one post in

I am trying to be able to serialize and upload multiple images to associate with each post.
This is my models.py
from django.conf import settings
from django.db import models
from django.db.models.signals import pre_save
from .utils import unique_slug_generator
class Painting(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default="", on_delete=models.CASCADE)
title = models.CharField(blank=False, null=False, default="", max_length=255)
slug = models.SlugField(blank=True, null=True)
style = models.CharField(blank=True, null=True, default="", max_length=255) #need to figure out why there is problem when this is False
description = models.TextField(blank=True, null=True, default="")
size = models.CharField(blank=True, null=True, default="", max_length=255)
artist = models.CharField(blank=True, null=True, default="", max_length=255)
price = models.DecimalField(blank=True, null=True, decimal_places=2, max_digits=20)
available = models.BooleanField(default=True)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
def __str__(self):
return self.title
class Meta:
ordering = ["-timestamp", "-updated"]
class PaintingPhotos(models.Model):
title = models.ForeignKey(Painting, default="", on_delete=models.CASCADE)
image = models.ImageField(upload_to='uploaded_paintings')
def pre_save_painting_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = unique_slug_generator(instance)
pre_save.connect(pre_save_painting_receiver, sender=Painting)
my serializers.py
from django.contrib.auth import get_user_model, authenticate, login, logout
from django.db.models import Q
from django.urls import reverse
from django.utils import timezone
from rest_framework import serializers
from .models import Painting, PaintingPhotos
User = get_user_model()
class UserPublicSerializer(serializers.ModelSerializer):
username = serializers.CharField(required=False, allow_blank=True, read_only=True)
class Meta:
model = User
fields = [
'username',
'first_name',
'last_name',
]
# # add PaintingImagesSerializer with the images model here
class PaintingPhotosSerializer(serializers.ModelSerializer):
class Meta:
model = PaintingPhotos
fields =[
'image'
]
#becareful here, if anyone submits a POST with an empty title, it will result in the empty slug, (which will mess up the url lookup since the title is the slug in this case)
#make title a required field in the actual interface, also remember to don't submit s POST with an empty title from the Django restframework directly
class PaintingSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='paintings-api:detail',
read_only=True,
lookup_field='slug'
)
user = UserPublicSerializer(read_only=True)
owner = serializers.SerializerMethodField(read_only=True)
image = PaintingPhotosSerializer(many=True, read_only=False)
class Meta:
model = Painting
fields = [
'url',
'user',
'title',
'style',
'description',
'size',
'artist',
'price',
'available',
'updated',
'timestamp',
'owner',
'slug',
'image',
]
def get_owner(self, obj):
request = self.context['request']
if request.user.is_authenticated:
if obj.user == request.user:
return True
return False
my views.py
from rest_framework.views import APIView
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.response import Response
from rest_framework import generics, permissions, pagination, status
from .models import Painting
from .permissions import IsOwnerOrReadOnly
from .serializers import PaintingSerializer
class PaintingPageNumberPagination(pagination.PageNumberPagination):
page_size = 5
page_size_query_param = 'size'
max_page_size = 20
def get_paginated_response(self, data):
author = False
user = self.request.user
if user.is_authenticated:
author = True
context = {
'next': self.get_next_link(),
'previous': self.get_previous_link(),
'count': self.page.paginator.count,
'author': author,
'results': data,
}
return Response(context)
class PaintingDetailAPIView(generics.RetrieveUpdateDestroyAPIView):
queryset = Painting.objects.all()
serializer_class = PaintingSerializer
lookup_field = 'slug'
permission_classes = [IsOwnerOrReadOnly]
class PaintingListCreateAPIView(generics.ListCreateAPIView):
queryset = Painting.objects.all()
serializer_class = PaintingSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
pagination_class = PaintingPageNumberPagination
def perform_create(self, serializer):
serializer.save(user=self.request.user)
I am getting this error:
AttributeError: Got AttributeError when attempting to get a value for field image on serializer PaintingSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the Painting instance.
Original exception text was: 'Painting' object has no attribute 'image'.
I am also not sure if I should create another app just to handle all the images.
Thanks so much in advance!
Your code looks similar enough to the docs here: https://www.django-rest-framework.org/api-guide/relations/#nested-relationships I can't see what exactly is wrong, but it could be that you haven't created a PaintingPhotos object so there is no model to serialize it. I mentioned in a comment that you can create this through the Django admin.
Hey guys I ended up finding the answer. This stackoverflow answer explains it really well: Multiple images per Model
where I messed up was not adding the related_name argument to my photo in my PaintingPhotos model.

ModelForm field attrs

According to the django docs, a modelform field accepts attrs. When I attempt to apply attrs I get
TypeError: init() got an unexpected keyword argument 'attrs'
The form I'm attempting to make is pretty simple, I just want to apply style to it. What am I doing wrong?
forms.py
from django import forms
from .models import ServiceReportModel
class ServiceReportCreateForm(forms.ModelForm):
class Meta:
model = ServiceReportModel
fields = [
'request_number',
'request_reason',
'actions_taken',
]
class ServiceReportUpdateForm(forms.ModelForm):
class Meta:
model = ServiceReportModel
fields = [
'report_number',
'request_number',
'request_reason',
'actions_taken',
]
widgets = {
'report_number': forms.CharField(attrs={'class': 'form-control'}),
'request_number': forms.CharField(attrs={'class': 'form-control'}),
'request_reason': forms.CharField(attrs={'class': 'form-control'}),
'actions_taken': forms.Textarea(attrs={'class': 'form-control'}),
}
views.py
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .forms import ServiceReportCreateForm, ServiceReportUpdateForm
from .models import ServiceReportModel
class ReportCreateView(CreateView):
form_class = ServiceReportCreateForm
model = ServiceReportModel
class ReportCreateView(UpdateView):
form_class = ServiceReportUpdateForm
model = ServiceReportModel
class ReportDeleteView(DeleteView):
model = ServiceReportModel
success_url = reverse_lazy('reports-list')
models.py
import uuid
from django.urls import reverse
from django.db import models
from django.forms import ModelForm
from main import models as main_models
from customers import models as customers_models
class ServiceReportModel(models.Model):
report_number = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
request_number = models.ForeignKey(ServiceRequestModel,
on_delete=models.PROTECT,
null=True,
related_name='s_report_number'
)
reported_by = models.ForeignKey(main_models.MyUser, editable=False, related_name='reports')
reported_date = models.DateTimeField(auto_now_add=True)
updated_by = models.ForeignKey(main_models.MyUser, editable=True, blank=True, null=True, related_name='+')
updated_date = models.DateTimeField(auto_now=True)
request_reason = models.CharField(max_length=255, null=True)
actions_taken = models.TextField()
def get_absolute_url(self):
return reverse('service-report', kwargs={'pk': self.pk})
Fields do not accept attrs, widgets do. And similarly the widgets dictionary expects widgets not fields. You should use TextInput.