Django Rest Framework serializing queryset fetchedn with value_list - python

I'm trying to query a specific column from my table. I've tried doing it with this
team_lenDeserialized = RolesInTeam.objects.values_list('user_id', flat=True).filter(academic_year_id=9).filter(deleted=0)
team_lenDict = RolesInTeamSerializer(team_lenDeserialized, many=True)
team_len = orderedDictToJSON(team_lenDict.data)
After that I run it through a function that converts it to JSON
def orderedDictToJSON(orderedDict):
return json.loads(json.dumps(orderedDict))
then I go and manipulate it further. However if I try to serialize and convert the team_lenDeserialized I get an error that states
AttributeError: Got AttributeError when attempting to get a value for field `user_id` on
serializer RolesInTeamSerializer`.
The serializer field might be named incorrectly and not match any
attribute or key on the `int` instance.
Original exception text was: 'int' object has no attribute 'user_id'.
This is my model for that table
class RolesInTeam(models.Model):
user_id = models.IntegerField()
team_id = models.IntegerField()
role_id = models.IntegerField()
deleted = models.IntegerField()
academic_year_id = models.IntegerField()
class Meta:
managed = False
db_table = 'roles_in_team'
and my serializer
class RolesInTeamSerializer(serializers.ModelSerializer):
class Meta:
model = RolesInTeam
fields = ['id', 'user_id', 'team_id', 'role_id', 'deleted', 'academic_year_id']
I have no clue what's happening or why it's not working.

You can only serialize models instances with a ModelSerializer, and values_list() returns a queryset of tuples, so when you try to use the serializer over the queryset, you get the error.
If you make a regular query (team_lenDeserialized = RolesInTeam.objects.filter(academic_year_id=9).filter(deleted=0)), you would be able to serialize team_lenDeserialized.

Related

How to create 2 objects from separate models with a single serializer and also retrieve them from the database with a single serializer in Django RF?

I have 3 models: Maker, Item and MakerItem that creates the relation between the items and their makers:
class Maker(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
name = models.CharField(max_length=100)
class Item(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
name = models.CharField(max_length=100)
class MakerItem(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
item_id = models.ForeignKey(Item, on_delete=models.CASCADE)
maker_id = models.ForeignKey(Maker, on_delete=models.CASCADE)
the items can have a random amount of makers.
I want to create both the Item and the MakerItem objects at the same time with a single set of data,
for example if a Maker with id = "abcd" already exists, and I go to /item and send a POST request with the following data:
{
"name": "item1",
"makers": [
{
"maker_id": "abcd"
}
]
}
I want the serializer to create the Item object and the MakerItem object.
I have achieved this, with the following setup:
views.py
class ItemListCreate(ListCreateAPIView):
queryset = Item.objects.all()
serializer_class = ItemSerializer
serializers.py
class ItemSerializer(serializers.ModelSerializer):
class MakerItemSerializer(serializers.ModelSerializer):
class Meta:
model = MakerItem
exclude = ['id', 'item_id']
makers = MakerItemSerializer(many=True)
class Meta:
model = Item
fields = ['id', 'name', 'makers']
def create(self, validated_data):
maker_item_data = validated_data.pop('makers')
item_instance = Item.objects.create(**validated_data)
for each in maker_item_data:
MakerItem.objects.create(
item_id=check_instance,
maker_id=each['maker_id']
)
return item_instance
but when Django tries to return the created object, it always gives me the error:
AttributeError at /item/
Got AttributeError when attempting to get a value for field `makers` on serializer `ItemSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Item` instance.
Original exception text was: 'Item' object has no attribute 'makers'.
What am I doing wrong?
Thanks
EDIT: To clarify, the objects get created and populate the database correctly, but when the browsable API that DRF provides tries to display the created object, it gives me the error above.
Change:
class ItemSerializer(serializers.ModelSerializer):
class MakerItemSerializer(serializers.ModelSerializer):
class Meta:
model = MakerItem
exclude = ['id', 'item_id']
makers = MakerItemSerializer(many=True)
To:
class ItemSerializer(serializers.ModelSerializer):
class MakerItemSerializer(serializers.ModelSerializer):
class Meta:
model = MakerItem
exclude = ['id', 'item_id']
makers = MakerItemSerializer(many=True, source="makeritem_set")
Hope this works!
For clarity, you're attempting to serialise the reverse relationship between MakerItem and Item for this serialiser.
This means that the attribute on your object is automatically set by Django as fieldname_set but you can override this behaviour by setting the related_name kwarg on the field and then makemigrations and migrate it.
In your case you would need to do:
maker_id = models.ForeignKey(Maker, on_delete=models.CASCADE, related_name="maker_items")
And then update the field in the Meta to match the new field name, this way you don't have to manually specify source. Because actually the attribute "makers" is misleading, due to the fact its actually the MakerItem, not the Maker itself.
See https://docs.djangoproject.com/en/3.2/ref/models/relations/ for further details about this behaviour.

How to serialize an array of objects in Django

I am working with Django and REST Framework and I am trying to create a get function for one of my Views and running into an error. The basic idea is that I am creating a market which can have multiple shops. For each shop there can be many products. So, I am trying to query all those products which exist in one shop. Once I get all those products I want to send it to my serializer which will finally return it as a JSON object. I have been able to make it work for one product but it does not work for an array of products.
My Product model looks like this:
'''Product model to store the details of all the products'''
class Product(models.Model):
# Define the fields of the product model
name = models.CharField(max_length=100)
price = models.IntegerField(default=0)
quantity = models.IntegerField(default=0)
description = models.CharField(max_length=200, default='', null=True, blank=True)
image = models.ImageField(upload_to='uploads/images/products')
category = models.ForeignKey(Category, on_delete=models.CASCADE, default=1) # Foriegn key with Category Model
store = models.ForeignKey(Store, on_delete=models.CASCADE, default=1)
''' Filter functions for the product model '''
# Create a static method to retrieve all products from the database
#staticmethod
def get_all_products():
# Return all products
return Product.objects.all()
# Filter the data by store ID:
#staticmethod
def get_all_products_by_store(store_id):
# Check if store ID was passed
if store_id:
return Product.objects.filter(store=store_id)
The product serializer that I built is as follows:-
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
and the view that I created is below
class StoreView(generics.ListAPIView):
"""Store view which returns the store data as a Json file.
"""
# Define class variables
serializer_class = StoreSerializer
# Manage a get request
def get(self, request):
# Get storeid for filtering from the page
store_id = request.GET.get('id')
if store_id:
queryset = Product.get_all_products_by_store(store_id)
# queryset = Product.get_all_products_by_store(store_id)[0]
else:
queryset = Product.get_all_products()
# queryset = Product.get_all_products()[0]
print("QUERYSET", queryset)
return Response(ProductSerializer(queryset).data)
The above view gives me the following error
AttributeError at /market
Got AttributeError when attempting to get a value for field `name` on serializer `ProductSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `QuerySet` instance.
Original exception text was: 'QuerySet' object has no attribute 'name'.
If instead queryset = Product.get_all_products_by_store(store_id), I use the line below it where I am only selecting the first option then I get the correct JSON response but if there multiple products then I am not able to serialize. How do I make it work?
If you want to serialize more than one record, either use ListSerializer instead, or pass many=True the the constructor of ModelSerializer:
return Response(ProductSerializer(queryset, many=True).data)
I found the answer thanks to #yedpodtrzitko for giving the direction.
I had to make two changes.
Define queryset outside the function
Pass many=True the the constructor of ModelSerializer
class StoreView(generics.ListAPIView):
"""Store view which returns the store data as a Json file.
"""
# Define class variables
queryset = []
serializer_class = StoreSerializer
# Manage a get request
def get(self, request):
# Get storeid for filtering from the page
store_id = request.GET.get('id')
if store_id:
queryset = Product.get_all_products_by_store(store_id)
else:
queryset = Product.get_all_products()
print("QUERYSET", queryset)
return Response(ProductSerializer(queryset, many = True).data)

Dont get field in result create method django rest framework

I have a method of creating clien. one of the body fields of my method is client_users which is not in my client model. He has a foreign key for clients. I overwrite the create method and it is ok. But when I return the method I have this error:
AttributeError: Got AttributeError when attempting to get a value for field `clientes_usuarios` on serializer `ClienteCreateSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Cliente` instance.
Original exception text was: 'Cliente' object has no attribute 'clientes_usuarios'.
My field in my serializer:
class ClienteCreateSerializer(serializers.ModelSerializer):
endereco_residencial = EnderecoSerializer(read_only=False)
endereco_cobranca = EnderecoSerializer(read_only=False,required=False)
contatos = ContatoClienteSerializer(many=True, read_only=False, required=False)
certificados = CertificadoSerializer(many=True, read_only=False, required=False)
email = serializers.EmailField(source='usuario.email')
cnpj = serializers.CharField(max_length=14, min_length=14, source='usuario.cpf_cnpj')
foto = serializers.CharField(required=False)
data_abertura = serializers.DateField(input_formats=settings.DATE_INPUT_FORMATS, required=False, allow_null=True)
clientes_usuarios = UsuarioClienteCreateSerializer(many=True,read_only=False)
I have outher methods like this and they works fine

DRF one to many serialization -- AttributeError from missing field

Error:
AttributeError at /stats/matches
Got AttributeError when attempting to get a value for field players on serializer MatchSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the Match instance.
Original exception text was: 'Match' object has no attribute 'players'.
Models:
Every Match has 10 players.
class Match(models.Model):
tournament = models.ForeignKey(Tournament, blank=True)
mid = models.CharField(primary_key=True, max_length=255)
mlength = models.CharField(max_length=255)
win_rad = models.BooleanField(default=True)
class Player(models.Model):
match = models.ForeignKey(Match, on_delete=models.CASCADE)
playerid = models.CharField(max_length=255, default='novalue')
# There is also a Meta class that defines unique_together but its omitted for clarity.
Serializers:
class PlayerSerializer(serializers.ModelSerializer):
class Meta:
model = Player
fields = "__all__"
class MatchSerializer(serializers.ModelSerializer):
players = PlayerSerializer(many=True)
class Meta:
model = Match
fields = ("mid","players")
The MatchSerializer search for a players attribute in Match's instance, but it couldn't find and you get the following error:
AttributeError at /stats/matches
Got AttributeError when attempting to get a value for field players on
serializer MatchSerializer. The serializer field might be named
incorrectly and not match any attribute or key on the Match instance.
Original exception text was: 'Match' object has no attribute 'players'.
In DRF serializer, a parameter called source will tell explicitly where to look for the data. So, change your MatchSerializer as follow:
class MatchSerializer(serializers.ModelSerializer):
players = PlayerSerializer(many=True, source='player_set')
class Meta:
model = Match
fields = ("mid", "players")
Hope it helps.
The problem here is that Match model has not an attribute called players, remember that you are trying to get backwards relationship objects, so you need to use players_set as field as django docs says.
You could solve this in Two Ways
1. Adding a source parameter to the PlayerSerializer
class MatchSerializer(serializers.ModelSerializer):
players = PlayerSerializer(many=True, source='player_set')
class Meta:
model = Match
fields = ("mid", "players")
2. Change the lookup-field
class MatchSerializer(serializers.ModelSerializer):
player_set = PlayerSerializer(many=True)
class Meta:
model = Match
fields = ("mid","player_set")

How to set a specific queryset on a ObjectSerializerModel

I'm developing a web system using Django 1.11 and the current version of Django Rest Framework which is 3.8.2.
I'm having a problem while serving a JSON of my models in this project. I need to pass a JSON that contains a specific attribute.
I'm gonna exemplify my serializers.py to make an easier understanding of my problem.
class LikertSerializerModel(serializers.ModelSerializer):
class Meta:
model = Likert
fields = ('id', 'escala')
class RespostaSerializerModel(serializers.ModelSerializer):
likerts = LikertSerializerModel(many = True, read_only = True)
class Meta:
model = Resposta
fields = ('id', 'resposta','tipo', 'foto', 'pergunta', 'qtd_escolhida', 'classificacao_escala', 'data', 'likerts')
class PerguntaSerializerModel(serializers.ModelSerializer):
respostas = RespostaSerializerModel(read_only=True ,many=True)
class Meta:
model = Pergunta
fields = ('id', 'pergunta', 'tipo_questao', 'questionario', 'respondida', 'data_inicial', 'data_final',
'obrigatoria', 'outros', 'ordem', 'qtd_max_caracteres', 'respostas')
class QuestionarioSerializerModel(serializers.ModelSerializer):
entrevistadores = UsuarioSerializer(many = True, read_only = True)
sub_administrador = UsuarioSerializer(read_only= True)
perguntas = PerguntaSerializerModel(many = True, read_only = True)
class Meta:
model = Questionario
fields = ('id', 'titulo', 'descricao', 'data', 'duracao', 'localizacao', 'sub_administrador', 'entrevistadores', 'perguntas')
THE PROBLEM: See, in the PerguntaSerializerModel I have this field "respostas". And the thing is, I only want to pass to that Pergunta Object, Resposta Objects that have a "tipo" attribute with a value of "alt" and are linked with that Pergunta Object.
PS: If you need an explanation of how those classes are linked, here it goes (we can ignore the Likert Object as it is useless to the problem):
A Resposta Object is linked by a ForeignKey to a single Pergunta Object, and a Pergunta Object is linked by a ForeignKey to a single Questionario Object.
A Questionario Object can have one or more Pergunta Objects and a Pergunta Object can have one or more Resposta Objects
When you do a GET in the rest URL the final JSON will start with the Questionario Object, and will cascade until the last Pergunta Object.
Please help me to find an answer, I wasn't able to find one in the REST documentation because I don't know what I need to look for.
Best regards.
What you need here is a SerializerMethodField(). You can read through the docs for more info on it.
Create a method on the serializer PerguntaSerializerModel that returns a filtered queryset of Resposta objects on the basis of attribute tipo.
Something like this should work fine:
class PerguntaSerializerModel(serializers.ModelSerializer):
respostas = serializers.SerializerMethodField()
class Meta:
model = Pergunta
fields = ('id', 'pergunta', 'tipo_questao', 'questionario', 'respondida', 'data_inicial', 'data_final',
'obrigatoria', 'outros', 'ordem', 'qtd_max_caracteres', 'respostas')
def get_respostas(self, obj):
reposta_qs = obj.filter(reposta__tipo='alt')
resposta_serailizer = RespostaSerializerModel(reposta_qs, read_only=True ,many=True)
return resposta_serailizer.data
Let me know if this helps !

Categories