Issue
This has floored me. I'm building a model with Django & REST API, and I'm having trouble rendering the DetailView for individual cars to the browser. The ListView works fine, but I'll include the code too since they are interlinked.
In particular, I can't get the get_object() function to work properly.
Here's the first approach I used
views.py
class CarDetailView(generics.RetrieveUpdateDestroyAPIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'car-detail.html'
queryset = Car.objects.all()
lookup_field = Car.id
@action(detail=True, renderer_classes=[TemplateHTMLRenderer])
def get(self, request, *args, **kwargs):
car = self.get_object()
return Response({
'car': car,
})
While the page rendered properly with no errors, none of the template tags worked. Only {{ car.model }}, which returned None.
So I changed the code to use a self.get_object()
class CarDetailView(generics.GenericAPIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'car-detail.html'
queryset = Car.objects.all()
lookup_field = Car.id
def get(self, request, id):
serializer_class = CarSerializer
car = self.get_object()
id = Car.id
return Response({
'car': car,
})
but that raises the Error:
"AttributeError at /rentals/car/43c9b98d-9f2d-473f-9c34-7b2e25630278 'CarDetailView' object has no attribute '_meta'"
Can you help with this? I've looked through most of the previous questions here (that's where I learned to pass the self into the get function etc...) but I'm still stuck.
Here's the code:
views.py
class CarListView(generics.ListCreateAPIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'car-list.html'
def get(self, request):
car_list = Car.objects.all()
serializer_class = CarSerializer
return Response({
'car_list': car_list,
})
class CarDetailView(generics.GenericAPIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'car-detail.html'
queryset = Car.objects.all()
lookup_field = Car.id
def get(self, request, id):
serializer_class = CarSerializer
car = self.get_object()
id = Car.id
return Response({
'car': car,
})
serializers.py
class CarSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='rental:car-detail', format='html')
class Meta:
model = Car
fields = ['url', 'id', 'manufacturer', 'model', 'owner', 'color', 'year']
models.py
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.SET_NULL, null=True)
model = models.ForeignKey(CarModel, on_delete=models.SET_NULL, null=True)
# Model year. Validator ensures that no model year can be set in the future.
year = models.IntegerField(validators=[MaxValueValidator(int(datetime.date.today().year) + 1)], null=True, default=2022)
...
Other fields for drivetrain, engine etc
...
# Fields for individual car in database
id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text="Automatically generated unique ID. Do not change.")
mileage = models.IntegerField(null=True)
insurance = models.ForeignKey(Insurance, on_delete=models.RESTRICT, null=True)
daily_rate = models.IntegerField(default=3500, help_text="Daily rate in Kenyan Shillings")
def __str__(self):
return f'{ self.manufacturer } { self.model }'
@property
def insurance_expired(self):
"""Determines if insurance is expired based on due date and current date."""
return bool(self.insurance.expiry_date and datetime.date.today() > self.insurance.expiry_date)
class Meta:
ordering = []
permissions = (("can_change_availability", "Set car as rented"),)
def get_absolute_url(self):
return reverse('rental:car-detail', args=[str(self.id)])
urls.py
from django.urls import path, include
from . import views
app_name = 'rental'
# API endpoints
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('cars/', views.CarListView.as_view(), name='car-list'),
path('car/<uuid:id>', views.CarDetailView.as_view(), name='car-detail'),
]
Error Code
AttributeError at /rentals/car/43c9b98d-9f2d-473f-9c34-7b2e25630278
'CarDetailView' object has no attribute '_meta'
Request Method: GET
Request URL: http://localhost:8000/rentals/car/43c9b98d-9f2d-473f-9c34-7b2e25630278
Django Version: 4.1.3
Exception Type: AttributeError
Exception Value:
'CarDetailView' object has no attribute '_meta'
Solution
Usually you should not implement the get()
method yourself, this is usually the task of the Django (API) views that will then pass it to the right renderer and template.
class CarDetailView(generics.RetrieveAPIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'car-detail.html'
serializer_class = CarSerializer
queryset = Car.objects.all()
In the template you can then use the fields of the car, so: {{ url }}
, {{ model }}
, {{ owner }}
, etc.
If you however plan to render a template, it might make more sense to work with a DetailView
[Django-doc] instead:
from django.views.generic.detail import DetailView
class CarDetailView(DetailView):
model = Car
template_name = 'car-detail.html'
In the template, you can then use {{ object.url }}
, {{ object.model }}
, etc. and you can also "follow" relations.
Answered By - Willem Van Onsem
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.