Поле «_id» ожидало номер, но получило «верх» в рамках Django Rest

#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-адресов, и шаблон URL getBlogPost -адреса соответствует тому, к которому вы в настоящее время пытаетесь получить доступ.

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 решит проблему.

Дополнительные услуги

  1. Не знаю, модели ли это.Автоматическое поле-лучший подход для хранения значения идентификатора. Обычно я буду использовать что-то вроде поля UUID
  2. Обычно, если вы используете некоторые необходимые и повторно используемые поля, такие как: 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)
 
  1. Хороший способ улучшить свои представления-использовать представления на основе классов
  2. Аккуратный подход к выполнению получает и фильтрует такие:
     BlogPostReview.objects.get(id=pk) 
    BlogPostReview.objects.filter(id=pk) 
 

Использует универсальный класс фильтров, реализован почти как класс сериализатора и отлично работает с представлениями на основе классов. Универсальный фильтр

Комментарии:

1. Да, это была именно та проблема, И я уже решил ее раньше, кстати, я ценю ваши усилия. Я изменил исходную функцию с .get(id=pk) to .all (), потому что мне просто нужен был список для этого конкретного URL-адреса, и теперь это работает нормально. Спасибо за публикацию. @Карлос

2. Приятно рад, что ваш код работает нормально @FaisalNazik !! Спасибо за комментарий.