#python #django #django-models #django-rest-framework
Вопрос:
Я уже видел здесь вопросы такого типа, но я не могу воспроизвести решение. Я пытаюсь получить лучшие посты в блогах от моей модели в соответствии с их рейтингом.
#models.py
class BlogPost(models.Model):
_id = models.AutoField(primary_key=True, editable=False)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
title = models.CharField(max_length=100, null=True, blank=True)
image = models.ImageField(null=True, blank=True,
default='/placeholder.png' )
description = models.TextField(null=True, blank=True, help_text="Description Here ")
rating = models.DecimalField(
max_digits=7, decimal_places=2, null=True, blank=True)
numReviews = models.IntegerField(null=True, blank=True, default=0)
createdAt = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.createdAt)
class BlogPostReview(models.Model):
blogpost = models.ForeignKey(BlogPost, on_delete=models.SET_NULL, null=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=200, null=True, blank=True)
rating = models.IntegerField(null=True, blank=True, default=0)
comment = models.TextField(null=True, blank=True)
createdAt = models.DateTimeField(auto_now_add=True)
_id = models.AutoField(primary_key=True, editable=False)
def __str__(self):
return str(self.rating)
Сериализатор
#serializers.py
class BlogPostReviewSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPostReview
fields = '__all__'
class BlogPostSerializer(serializers.ModelSerializer):
reviews = BlogPostReviewSerializer( many=True, read_only=True)
class Meta:
model = BlogPost
fields = '__all__'
def get_reviews(self, obj):
reviews = obj.blogpostreview_set.all()
serializer = BlogPostReviewSerializer(reviews, many=True)
return serializer.data
Теперь перейдем к взглядам … Проблемный вопрос
from django.shortcuts import render
# Create your views here.
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.response import Response
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .models import BlogPost, BlogPostReview
from .serializers import BlogPostSerializer
@api_view(['GET'])
def getBlogPosts(request):
query = request.query_params.get('keyword')
if query == None:
query = ''
blogposts = BlogPost.objects.filter(
title__icontains=query).order_by('-createdAt')
page = request.query_params.get('page')
paginator = Paginator(blogposts, 8)
try:
blogposts = paginator.page(page)
except PageNotAnInteger:
blogposts = paginator.page(1)
except EmptyPage:
blogposts = paginator.page(paginator.num_pages)
if page == None:
page = 1
page = int(page)
serializer = BlogPostSerializer(blogposts, many=True)
return Response({'blogposts': serializer.data, 'page': page, 'pages': paginator.num_pages})
@api_view(['GET'])
def getTopBlogPosts(request):
blogposts = BlogPost.objects.filter(rating__gte=4.0).order_by('-rating')[0:10]
serializer = BlogPostSerializer(blogposts, many=True)
return Response(serializer.data)
@api_view(['GET'])
def getBlogPost(request, pk):
blogpost = BlogPost.objects.get(_id=pk)
serializer = BlogPostSerializer(blogpost, many=False)
return Response(serializer.data)
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def createBlogPostReview(request, pk):
user = request.user
blogpost = BlogPost.objects.get(_id=pk)
data = request.data
# 1 - Review already exists
alreadyExists = blogpost.review_set.filter(user=user).exists()
if alreadyExists:
content = {'detail': 'Product already reviewed'}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
# 2 - No Rating or 0
elif data['rating'] == 0:
content = {'detail': 'Please select a rating'}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
# 3 - Create review
else:
review = BlogPostReview.objects.create(
user=user,
blogpost=blogpost,
name=user.first_name,
rating=data['rating'],
comment=data['comment'],
)
reviews = blogpost.review_set.all()
blogpost.numReviews = len(reviews)
total = 0
for i in reviews:
total = i.rating
blogpost.rating = total / len(reviews)
blogpost.save()
return Response('Review Added')
URLs
from django.urls import path
from . import views as views
urlpatterns = [
path('', views.getBlogPosts, name="blogposts"),
path('<str:pk>/', views.getBlogPost, name="blogpost"),
path('top/', views.getTopBlogPosts, name='top-blogposts'),
path('<str:pk>/reviews/', views.createBlogPostReview, name="create-review"),
]
Когда я пытаюсь загрузить верхние посты, появляется эта ошибка
[21/Jun/2021 21:27:43] "GET /api/blog/top/ HTTP/1.1" 500 165476
[21/Jun/2021 21:27:48] "GET /api/blog/ HTTP/1.1" 200 5256
Internal Server Error: /api/blog/top/
Traceback (most recent call last):
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangodbmodelsfields__init__.py", line 1823, in get_prep_value
return int(value)
ValueError: invalid literal for int() with base 10: 'top'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangocorehandlersexception.py", line 47, in inner
response = get_response(request)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangocorehandlersbase.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangoviewsdecoratorscsrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangoviewsgenericbase.py", line 70, in view
return self.dispatch(request, *args, **kwargs)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesrest_frameworkviews.py", line 509, in dispatch
response = self.handle_exception(exc)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesrest_frameworkviews.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesrest_frameworkviews.py", line 480, in raise_uncaught_exception
raise exc
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesrest_frameworkviews.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesrest_frameworkdecorators.py", line 50, in handler
return func(*args, **kwargs)
File "E:eCommerce_Projectsremote-hospitalblogviews.py", line 48, in getBlogPost
blogpost = BlogPost.objects.get(_id=pk)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangodbmodelsmanager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangodbmodelsquery.py", line 424, in get
clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangodbmodelsquery.py", line 941, in filter
return self._filter_or_exclude(False, args, kwargs)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangodbmodelsquery.py", line 961, in _filter_or_exclude
clone._filter_or_exclude_inplace(negate, args, kwargs)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangodbmodelsquery.py", line 968, in _filter_or_exclude_inplace
self._query.add_q(Q(*args, **kwargs))
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangodbmodelssqlquery.py", line 1396, in add_q
clause, _ = self._add_q(q_object, self.used_aliases)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangodbmodelssqlquery.py", line 1415, in _add_q
child_clause, needed_inner = self.build_filter(
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangodbmodelssqlquery.py", line 1350, in build_filter
condition = self.build_lookup(lookups, col, value)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangodbmodelssqlquery.py", line 1196, in build_lookup
lookup = lookup_class(lhs, rhs)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangodbmodelslookups.py", line 25, in __init__
self.rhs = self.get_prep_lookup()
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangodbmodelslookups.py", line 77, in get_prep_lookup
return self.lhs.output_field.get_prep_value(self.rhs)
File "C:UsersLENOVOAppDataLocalProgramsPythonPython39libsite-packagesdjangodbmodelsfields__init__.py", line 1825, in get_prep_value
raise e.__class__(
ValueError: Field '_id' expected a number but got 'top'.
[21/Jun/2021 21:27:55] "GET /api/blog/top/ HTTP/1.1" 500 165476
Нужна помощь, чтобы удалить эту ошибку и любые другие способы достижения этой функциональности, а также то, что я делаю неправильно в этом коде.
Комментарии:
1. Пожалуйста, добавьте полный отчет об ошибке в свой вопрос.
2. @AbdulAzizBarkat добавлена полная обратная запись ошибок.
3. Здесь используется какой-то другой вид, чем вы показываете. Представление
getBlogPost
вызывается в соответствии с ошибкой.4. @FaisalNazik к какому именно виду вы пытаетесь получить доступ? Как я уже сказал, используется ваше представление
getBlogPost
, скорее всего, это проблема с вашими шаблонами URL-адресов, и шаблон URLgetBlogPost
-адреса соответствует тому, к которому вы в настоящее время пытаетесь получить доступ.5. Ну
<str:pk>/
top/
, не так ли?top
является строкой , и поэтому используется представлениеgetBlogPost
, поэтому вы получаете сообщение об ошибке. Решение состоит в том, чтобы лучше упорядочить свои URL-шаблоны или написать лучшие и более описательные URL-адреса.
Ответ №1:
Я опубликую ваше решение, а также некоторые мысли о вашем коде.
Основная проблема
views.py
@api_view(['GET'])
def getBlogPost(request, pk):
blogpost = BlogPost.objects.get(_id=pk) ## << HERE >> ##
serializer = BlogPostSerializer(blogpost, many=False)
return Response(serializer.data)
urls.py
path('', views.getBlogPosts, name="blogposts"),
path('<str:pk>/', views.getBlogPost, name="blogpost"), ## << HERE >> ##
path('top/', views.getTopBlogPosts, name='top-blogposts'),
path('<str:pk>/reviews/', views.createBlogPostReview, name="create-review"), ## << SAME PROBLEM HERE >> ##
В резюме ваша попытка передать тип как pk witch-это _id вашего модельного блога и является автополем. Вероятно, переход с ot решит проблему.
Дополнительные услуги
- Не знаю, модели ли это.Автоматическое поле-лучший подход для хранения значения идентификатора. Обычно я буду использовать что-то вроде поля UUID
- Обычно, если вы используете некоторые необходимые и повторно используемые поля, такие как: id, created_at, updated_at и т.д. Способ сделать это-создать базовую модель, обычно реализуемую в приложении yourproject.core. И используйте его в качестве своей модели по наследству, как это:
core.models.py
class BaseModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_at = models.DateTimeField()
updated_at = models.DateTimeField()
#
# Continue your BaseModel
#
И в ваших моделях
class BlogPostReview(BaseModel):
blogpost = models.ForeignKey(BlogPost, on_delete=models.SET_NULL, null=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=200, null=True, blank=True)
rating = models.IntegerField(null=True, blank=True, default=0)
comment = models.TextField(null=True, blank=True)
def __str__(self):
return str(self.rating)
- Хороший способ улучшить свои представления-использовать представления на основе классов
- Аккуратный подход к выполнению получает и фильтрует такие:
BlogPostReview.objects.get(id=pk)
BlogPostReview.objects.filter(id=pk)
Использует универсальный класс фильтров, реализован почти как класс сериализатора и отлично работает с представлениями на основе классов. Универсальный фильтр
Комментарии:
1. Да, это была именно та проблема, И я уже решил ее раньше, кстати, я ценю ваши усилия. Я изменил исходную функцию с
.get(id=pk) to
.all (), потому что мне просто нужен был список для этого конкретного URL-адреса, и теперь это работает нормально. Спасибо за публикацию. @Карлос2. Приятно рад, что ваш код работает нормально @FaisalNazik !! Спасибо за комментарий.