DRF: How to add annotates when create an object manually? - python

Currently I'm trying to create an expected json to use in my test:
#pytest.mark.django_db(databases=['default'])
def test_retrieve_boards(api_client):
board = baker.make(Board)
objs = BoardSerializerRetrieve(board)
print(objs.data)
url = f'{boards_endpoint}{board.id}/'
response = api_client().get(url)
assert response.status_code == 200
But i'm receiving the following error:
AttributeError: Got AttributeError when attempting to get a value for field `cards_ids` on serializer `BoardSerializerRetrieve`.
E The serializer field might be named incorrectly and not match any attribute or key on the `Board` instance.
E Original exception text was: 'Board' object has no attribute 'cards_ids'
Currently cards_idsare added on my viewSet on get_queryset method:
def get_queryset(self):
#TODO: last update by.
#TODO: public collections.
"""Get the proper queryset for an action.
Returns:
A queryset object according to the request type.
"""
if "pk" in self.kwargs:
board_uuid = self.kwargs["pk"]
qs = (
self.queryset
.filter(id=board_uuid)
.annotate(cards_ids=ArrayAgg("card__card_key"))
)
return qs
return self.queryset
and this is my serializer:
class BoardSerializerRetrieve(serializers.ModelSerializer):
"""Serializer used when retrieve a board
When retrieve a board we need to show some informations like last version of this board
and the cards ids that are related to this boards, this serializer will show these informations.
"""
last_version = serializers.SerializerMethodField()
cards_ids = serializers.ListField(child=serializers.IntegerField())
def get_last_version(self, instance):
last_version = instance.history.first().prev_record
return HistoricalRecordSerializer(last_version).data
class Meta:
model = Board
fields = '__all__'
what is the best way to solve it? I was thinking in create a get_cards_ids method on serializer and remove annotate, but I don't know how to do it and justing googling it now. rlly don't know if this is the correct way to do it.

Test the view, not the serializer, i.e. remove BoardSerializerRetrieve(board) from your test code.
cards_ids is annotated on ViewSet level. The annotated queryset is then passed to serializer.
#pytest.mark.django_db(databases=['default'])
def test_retrieve_boards(api_client):
board = baker.make(Board)
url = f'{boards_endpoint}{board.id}/'
response = api_client().get(url)
assert response.status_code == 200
Also, instead of building the URL manually with url = f'{boards_endpoint}{board.id}/', consider using reverse, e.g. url = reverse("path-name", kwargs={"pk": board.id}).

Related

Django rest framework, ¿What would be the best way to resolve the error, " index list creation error must be integer or chunked, not str" .?

There is a possibility to solve my problem, please.
I have 3 molds, of which 2 are related.
I am using create on ModelViewSet, which sent the data but I am getting the following error.
(list indexes must be integer or chunked, not str)
Of course I am trying to create a list of objects, but.
¿How would you develop the syntax? With the use of For?
ModelViewSet
class CreateApiModelViewSet(viewsets.ModelViewSet):
serializer_class = EDFSerializer
def get_queryset(self):
dano = models.Dano.objects.all()
return dano
def create(self, request, *args, **kwargs):
data = request.data
new_evento = models.Evento.objects.create(
tabla=data["evento"]["tabla"],
usuario=models.Usuario.objects.filter(user_id=data["evento"]["usuario"]).first(),
patio=models.Patio.objects.filter(id=data["evento"]["patio"]).first()
)
new_evento.save()
# New Dano
new_dano = models.Dano.objects.create(
evento=new_evento,
observacion=data["observacion"])
new_dano.save()
# Model FotoDano With Error.. :(
foto = []
for fotos in foto:
for f in data["fotodanodetail"]["foto"][0]:
foto_obj = models.FotoDano.objects.get(
foto=data["fotodanodetail"]["foto"],
dano=new_dano)
new_foto_dano.foto.add(foto_obj)
# Comment
# new_foto_dano = models.FotoDano.objects.create(
# #id=data["fotodanodetail"]["id"],
# #foto=data["fotodanodetail"]["foto"],
# dano=new_dano)
# new_foto_dano.save()
serializer = EDFSerializer(new_evento)
return Response(serializer.data)
Serializer
# Import Base64
from drf_extra_fields.fields import Base64ImageField
class EDFSerializer(serializers.ModelSerializer):
fotodanodetail = Base64ImageField(required=False)
evento = CrearEventoSerializer()
fotodanodetail = FotoDanoFiltroSerializer(many=True)
class Meta:
model = models.Dano
fields = ('evento','observacion','fotodanodetail')
Postman
Based on DRF when you have writeable nested serializer best way is override .create method of your serializer rather than viewset
for more information checkout drf docs here
All data in request.data have str type you must cast like int(data["evento"]["usuario"])

get() returned more than one-- Django Restframework

I have a Django model which needs to have more than 1 images and more than 1 files (numbers may vary as per requirement), for which I adjusted my Admin Panel accordingly like this
models.py
class MasterIndividualMembers(models.Model):
individualmemberId = models.CharField(primary_key=True, max_length=100, default=1)
...
...
def __str__(self):
return self.firstname + " " + self.lastname
class IndividualMemberPhotos(models.Model):
individualmemberId = models.ForeignKey(MasterIndividualMembers, default=None,on_delete=models.CASCADE)
image = models.ImageField(upload_to="individualmemberphotos/")
class IndividualMemberCatalogue(models.Model):
individualmemberId = models.ForeignKey(MasterIndividualMembers, default=None,on_delete=models.CASCADE)
files = models.FileField(upload_to="individualmembercatalogue/")
admin.py
class IndividualMemberPhotosAdmin(admin.StackedInline):
model = IndividualMemberPhotos
class IndividualMemberCatalogueAdmin(admin.StackedInline):
model = IndividualMemberCatalogue
#admin.register(MasterIndividualMembers)
class MasterIndividualMembersAdmin(admin.ModelAdmin):
inlines = [IndividualMemberPhotosAdmin,IndividualMemberCatalogueAdmin]
class Meta:
model = MasterIndividualMembers
For the views I simply make a function to provide details of all the Images, Document and that User
views.py
#csrf_exempt
#api_view(['POST'])
#permission_classes([IsAuthenticated])
def get_individualmember(request):
if request.method == 'POST':
try:
individualmemberId = request.POST.get('individualmemberId')
result = {}
result['individualMemberDetails'] = json.loads(serializers.serialize('json', [MasterIndividualMembers.objects.get(individualmemberId=individualmemberId)]))
result['individualPhotoDetails'] = json.loads(serializers.serialize('json', IndividualMemberPhotos.objects.filter(individualmemberId__individualmemberId = individualmemberId)))
result['individualCatalogueDetails'] = json.loads(serializers.serialize('json', IndividualMemberCatalogue.objects.filter(individualmemberId__individualmemberId = individualmemberId)))
except Exception as e:
return HttpResponseServerError(e)
Problem: While fetching the details for any individual member, it throws an error get() returned more than one IndividualMemberPhotos -- it returned 2!, which is expected to have more than 1 objects.
How can I make the Restframework to provide me details of all image object together.
Instead of using get() which strictly returns a single element, use filter() which returns 0 or more elements.
As documented in https://docs.djangoproject.com/en/3.2/topics/db/queries/#retrieving-a-single-object-with-get
filter() will always give you a QuerySet, even if only a single object
matches the query - in this case, it will be a QuerySet containing a
single element.
If you know there is only one object that matches your query, you can
use the get() method on a Manager which returns the object directly:
The behavior you are experiencing is actually documented here https://docs.djangoproject.com/en/3.2/ref/models/querysets/#django.db.models.query.QuerySet.get
If get() finds more than one object, it raises a
Model.MultipleObjectsReturned exception:

Django Model reverse relationship having a string with the name of it

I want to implement a Django Rest Framework view that returns the dependencies tree of a model instance object. This is the code for such view:
class RoomTypeDependencies(viewsets.ViewSet):
def list(self, request, pk):
room_type = models.RoomType.objects.get(pk=pk)
dependency_tree = self.get_object_dependencies(room_type)
return Response(dependency_tree)
def get_object_dependencies(self, instance):
fields = instance.__class__._meta.get_fields()
dependencies_to_return = []
for field in fields:
print(field.name)
if field.__class__.__name__ == 'ManyToOneRel':
dependency_to_return = []
dependent_instances = getattr(instance, field.name)
for dependent_instance in dependent_instances:
dependency_to_return.append(self.get_object_dependencies(dependent_instance))
dependencies_to_return.append({field.__class__.__name__: dependency_to_return})
return Response({str(instance): dependencies_to_return})
Everything seems to work, but I expected getattr(instance, field.name) to return the dependent instances corresponding to the reverse relationship, just like using model_object_instance.reverse_relationshio_name pattern, but it returns a RelatedManager object instead. The problem in my case is that I have the reverse relationship name in a string variable (field.name).

I want delete serializer's content to show

I want delete serializer's content to show.I am using Django Rest Framework.I am making a system return Json of serializers.Furthermore,I did not want to show user_id data. I wrote in views.py
class InfoViews(viewsets.ModelViewSet):
queryset = Info.objects.all()
serializer_class = InfoSerializer
lookup_field = 'id'
def update(self,request, *args, **kwargs):
obj = UserInfo.objects.get(pk=kwargs['id'])
data = request.data
info_serializers = InfoSerializer(obj, data = data)
if info_serializers.is_valid(raise_exception=True):
info_serializers.save()
del info_serializers.data['user_id']
return JsonResponse(info_serializers.data)
Now all son data is shown.What is wrong in my code?How should I fix this?
You can specify in the serializers which fields you want to include or exclude in the response:
http://www.django-rest-framework.org/api-guide/serializers/#specifying-which-fields-to-include
look for : Specifying which fields to include
hope this helpes
First Clone the object into a new variable and then pop the key.
if info_serializers.is_valid(raise_exception=True):
info_serializers.save()
import copy
new = copy.deepcopy(info_serializers.data)
new.pop('user_id', None)
return JsonResponse(new)

Foreign Key Resource from dynamic field

I've got an API endpoint called TrackMinResource, which returns the minimal data for a music track, including the track's main artist returned as an ArtistMinResource. Here are the definitions for both:
class TrackMinResource(ModelResource):
artist = fields.ForeignKey(ArtistMinResource, 'artist', full=True)
class Meta:
queryset = Track.objects.all()
resource_name = 'track-min'
fields = ['id', 'artist', 'track_name', 'label', 'release_year', 'release_name']
include_resource_uri = False
cache = SimpleCache(public=True)
def dehydrate(self, bundle):
bundle.data['full_artist_name'] = bundle.obj.full_artist_name()
if bundle.obj.image_url != settings.NO_TRACK_IMAGE:
bundle.data['image_url'] = bundle.obj.image_url
class ArtistMinResource(ModelResource):
class Meta:
queryset = Artist.objects.all()
resource_name = 'artist-min'
fields = ['id', 'artist_name']
cache = SimpleCache(public=True)
def get_resource_uri(self, bundle_or_obj):
return '/api/v1/artist/' + str(bundle_or_obj.obj.id) + '/'
The problem is, the artist field on Track (previously a ForeignKey) is now a model method called main_artist (I've changed the structure of the database somewhat, but I'd like the API to return the same data as it did before). Because of this, I get this error:
{"error": "The model '<Track: TrackName>' has an empty attribute 'artist' and doesn't allow a null value."}
If I take out full=True from the 'artist' field of TrackMinResource and add null=True instead, I get null values for the artist field in the returned data. If I then assign the artist in dehydrate like this:
bundle.data['artist'] = bundle.obj.main_artist()
...I just get the artist name in the returned JSON, rather than a dict representing an ArtistMinResource (along with the associated resource_uris, which I need).
Any idea how to get these ArtistMinResources into my TrackMinResource? I can access an ArtistMinResource that comes out fine using the URL endpoint and asking for it by ID. Is there a function for getting that result from within the dehydrate function for TrackMinResource?
You can use your ArtistMinResource in TrackMinResource's dehydrate like this (assuming that main_artist() returns the object that your ArtistMinResource represents):
artist_resource = ArtistMinResource()
artist_bundle = artist_resource.build_bundle(obj=bundle.obj.main_artist(), request=request)
artist_bundle = artist_resource.full_dehydrate(artist_bundle)
artist_json = artist_resource.serialize(request=request, data=artist_bundle, format='application/json')
artist_json should now contain your full artist representation. Also, I'm pretty sure you don't have to pass the format if you pass the request and it has a content-type header populated.

Categories

Resources