I have to serialize data for a django rest framework application, some of values will be on Brazilian currency format like 1.234,45.
How can I bind those number to work with django rest serializer and django models
My model:
class Produto(models.Model):
prod_codigo = models.AutoField(db_column='prod_codigo', primary_key=True)
prod_alias = models.CharField(db_column='prod_alias', max_length=50, null=False)
prod_descricao = models.CharField(db_column='prod_descricao', max_length=255, null=False)
prod_valor_venda = models.DecimalField(db_column='prod_valor_venda', max_digits=13, decimal_places=2)
prod_valor_compra = models.DecimalField(db_column='prod_valor_compra', max_digits=13, decimal_places=2)
prod_peso_b = models.DecimalField(db_column='prod_peso_b', max_digits=13, decimal_places=2)
prod_peso_l = models.DecimalField(db_column='prod_peso_l', max_digits=13, decimal_places=2)
My serializer:
class ProdutoSerializer(serializers.Serializer):
prod_codigo = serializers.IntegerField(read_only=True)
prod_alias = serializers.CharField(required=False, allow_blank=True)
prod_descricao = serializers.CharField(required=True, allow_blank=True)
prod_valor_venda = serializers.DecimalField(max_digits=13, decimal_places=2)
prod_valor_compra = serializers.DecimalField(max_digits=13, decimal_places=2)
prod_peso_b = serializers.DecimalField(max_digits=13, decimal_places=2)
prod_peso_l = serializers.DecimalField(max_digits=13, decimal_places=2)
class Meta:
model = Produto
def create(self, validated_data):
return Produto.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.prod_codigo = validated_data.get('prod_codigo', instance.prod_codigo)
instance.prod_alias = validated_data.get('prod_alias', instance.prod_alias)
instance.prod_descricao = validated_data.get('prod_descricao', instance.prod_descricao)
instance.prod_valor_venda = validated_data.get('prod_valor_venda', instance.prod_valor_venda)
instance.prod_valor_compra = validated_data.get('prod_valor_compra', instance.prod_valor_compra)
instance.prod_peso_b = validated_data.get('prod_peso_b', instance.prod_peso_b)
instance.prod_peso_l = validated_data.get('prod_peso_l', instance.prod_peso_l)
instance.prod_peso_q = validated_data.get('prod_peso_q', instance.prod_peso_q)
instance.save()
return instance
If I understand it correctly and 1.234,45 equals to 1.23445 why not handle this on the client side? In my opinion this is the best way to handle to handle format issues.
If you still wanna do it in your backend app you can override to_representation(self, instance)
or use SerializerMethodField like the code below:
class ProdutuSerializer(serializer.ModelSerializer):
...
your_field = serializers.SerializerMethodField()
def get_your_field_name(self, instance):
""" stringify your field """
return "some format" % instance.field
Hope that it helps!
Related
My models:
class Ingredient(models.Model):
BASE_UNIT_CHOICES = [("g", "Grams"), ("ml", "Mililiters")]
CURRENCY_CHOICES = [("USD", "US Dollars"), ("EUR", "Euro")]
ingredient_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
base_unit = models.CharField(max_length=2, choices=BASE_UNIT_CHOICES)
cost_per_base_unit = models.FloatField()
currency = models.CharField(
max_length=3, choices=CURRENCY_CHOICES, default="EUR")
def __str__(self):
return self.name
class RecipeIngredient(models.Model):
quantity = models.FloatField()
ingredient_id = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
def __str__(self):
return f"{self.quantity} / {self.ingredient_id}"
class Recipe(models.Model):
recipe_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
ingredients = models.ManyToManyField(RecipeIngredient)
date_created = models.DateTimeField('Date Created')
def __str__(self):
return f"{self.name}, {self.ingredients}"
When I use the admin page, it has this + button that allows me to create new ingredient/quantity combinations
like this
But when I try to use it from a form in my code it looks like
this
Here is my form code:
class AddRecipeForm(forms.ModelForm):
class Meta:
model = Recipe
fields = ['name', 'ingredients', 'date_created']
You should write the 'widgets' for each field in you Form that need configuration.
Check the documentation 'Widgets in forms', or even, you can define your own Widgets.
Note: IF INFORMATION BELOW IS NOT CLEAR TO UNDERSTAND - PLEASE ASK ME, I WILL UPDATE AND POST INFORMATION YOU NEED | It is important for me
In Warehouse(models.Model) I have amount attribute and
in ChosenProduct(models.Model) - quantity
I'm trying to get amount in Warehouse through chosen_products instance in App_formSerializer to add the quantity of chosen_product
But I can not get the chosen_products objects from instance
--> below Out:
class WarehouseSerializer(serializers.ModelSerializer):
category_name = serializers.ReadOnlyField(
source='category_product.category_name')
posted_user = serializers.ReadOnlyField(
source='posted_user.username')
class Meta:
model = Warehouse
fields = ['id', 'category_product', 'category_name', 'condition',
'product_name', 'amount', 'barcode', 'f_price', 'created_at', 'updated_at', 'posted_user']
class ChosenProductSerializer(serializers.ModelSerializer):
product_info = WarehouseSerializer(source='product', read_only=True)
period_info = Product_periodSerializer(source='period', read_only=True)
class Meta:
model = ChosenProduct
exclude = ('app_form',)
class App_formSerializer(serializers.ModelSerializer):
chosen_products = ChosenProductSerializer(many=True)
def update(self, instance, validated_data):
instance.terminated = validated_data.get('terminated', instance.terminated)
if instance.terminated == True :
print('-----------TRUE--------------------')
print(instance.chosen_products)
print('-----------PRINT--------------------')
instance.save()
return instance
class Meta:
model = App_form
fields = '__all__'
Out
-----------TRUE--------------------
creditapi.ChosenProduct.None
-----------PRINT--------------------
QUESTION UPDATED
models.py
class Warehouse(models.Model):
category_product = models.ForeignKey(
Category_product, on_delete=models.CASCADE)
product_name = models.CharField(max_length=200, unique=True)
condition = models.BooleanField(default=False)
amount = models.IntegerField()
barcode = models.BigIntegerField()
f_price = models.CharField(max_length=255, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
posted_user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
def __str__(self):
return self.product_name
class App_form(models.Model):
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,12}$', message="Phone number must be entered in the format: '998981234567'. Up to 12 digits allowed.")
terminated = models.BooleanField(default=False)
name = models.CharField(max_length=150)
phone_number = models.CharField(validators=[phone_regex], max_length=13)
def __str__(self):
return self.surname
class ChosenProduct(models.Model):
product = models.ForeignKey(Warehouse, on_delete=models.CASCADE)
quantity = models.IntegerField()
app_form = models.ForeignKey(App_form, on_delete=models.CASCADE, related_name='chosen_products')
def __str__(self):
return self.product.product_name
If you write instance.chose_products you access the manager, not the QuerySet that contains the items. You can use .all() to obtain the QuerySet with all the objects:
print(instance.chosen_products.all())
If you access a ForeignKey in reverse, you have a manager, since zero, one, or more elements can refer to the instance.
You can for example aggregate over the chose_products, for example if you want to retrieve the number of related chose_products, you can use:
print(instance.chosen_products.count())
I would however advise not store (aggregated) data in the App_form, but aggregate data when you need it. Data duplication is an anti-pattern, and it turns out it is hard to keep data in sync.
I have two models related through a one to one field, and I want to create a REST services that manages them both as they where one. The post works perfectly so far, and new instances are created in both models as they where one, but the put method just does nothing. It raises no error or anything, it just leaves the data unchanged.
These are my models:
class Company(models.Model):
name = models.CharField(max_length=40)
legal_name = models.CharField(max_length=100)
url = models.URLField()
address = models.TextField(max_length=400)
def __str__(self):
return "[{}]{}".format(self.id, self.name)
class Hotel(models.Model):
company = models.OneToOneField('Company', on_delete=models.CASCADE, primary_key=True)
code = models.CharField(max_length=10)
city = models.ForeignKey('City', on_delete=models.PROTECT, related_name='city_hotels')
category = models.ForeignKey('HotelCategory', on_delete=models.PROTECT, related_name='category_hotels')
capacity = models.IntegerField()
position = models.DecimalField(max_digits=11, decimal_places=2, default=0.00)
in_pickup = models.BooleanField(default=False)
def __str__(self):
return self.company.name
This is my ViewSet:
class HotelViewSet(viewsets.ModelViewSet):
queryset = models.Hotel.objects.all()
serializer_class = serializers.HotelSerializer
These are my serializers:
class CompanySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.Company
fields = ('id', 'name', 'legal_name', 'url', 'address')
class HotelSerializer(serializers.HyperlinkedModelSerializer):
company = CompanySerializer()
class Meta:
model = models.Hotel
fields = ('company', 'code', 'city', 'category', 'capacity', 'position', 'in_pickup')
def create(self, validated_data):
company_data = validated_data.pop('company')
new_company=models.Company.objects.create(**company_data)
hotel = models.Hotel.objects.create(company=new_company, **validated_data)
return hotel
def update(self, instance, validated_data):
company = models.Company(
id=instance.company.id,
name=instance.company.name,
legal_name=instance.company.legal_name,
url=instance.company.url,
address=instance.company.address
)
company.save()
instance.save()
return instance
I found that instance carries the original data, and validated_data carries the new data. I was saving the original data back.
I had to replace instance data with validated_data data and then save instance:
def update(self, instance, validated_data):
company_data = validated_data.pop('company')
company = models.Company.objects.get(id=instance.company.id)
company.name = company_data.get('name')
company.legal_name = company_data.get('legal_name')
company.tax_id = company_data.get('tax_id')
company.url = company_data.get('url')
company.address = company_data.get('address')
instance.company = company
instance.code = validated_data.get('code', instance.code)
instance.city = validated_data.get('city', instance.city)
instance.category = validated_data.get('category', instance.category)
instance.capacity = validated_data.get('capacity', instance.capacity)
instance.position = validated_data.get('position', instance.position)
instance.in_pickup = validated_data.get('in_pickup', instance.in_pickup)
instance.is_active = validated_data.get('is_active', instance.is_active)
company.save()
instance.save()
return instance
put method works handsomely now.
You are creating a new instance of company instead of updating the one that belongs to the Hotel instance.
company = Company.objects.get(id=instance.company.id)
company.name = instance.company.name
company.legal_name = instance.company.name
company.url = instance.company.url
company.address = instance.company.address
company.save()
instance.save()
I want to get all customer data and responses and also remarks.
This is model.py
class Customer(models.Model):
name = models.CharField(max_length=200)
email_address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=20)
age = models.SmallIntegerField(default=14)
remarks = models.ManyToManyField(Remark,null=True,blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.id)
class Response(models.Model):
question = models.ForeignKey(Question)
customer = models.ForeignKey(Customer)
response_text = models.CharField(max_length=100, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField()
def __str__(self):
return str(self.id)
This is serializers.py
class ResponseSerializer(ModelSerializer):
class Meta:
model = Response
fields = '__all__'
class RemarksSerializer(ModelSerializer):
class Meta:
model = Remark
fields = '__all__'
class CustomerInformationSerializer(ModelSerializer):
remarks = RemarksSerializer(many=True)
responses = serializers.SerializerMethodField()
def get_responses(self, obj):
responses = Response.objects.filter(customer=obj)
return ResponseSerializer(responses, many=True)
class Meta:
model = Customer
fields = ('name', 'email_address', 'phone_number', 'age', 'remarks', 'responses')
This is services.py
def customer_information(company_id=1):
cus = Customer.objects.filter(remarks__company_id=company_id)
return CustomerInformationSerializer(cus, many=True).data
This is views.py
class CustomerInformationView(APIView):
def get(self, request):
company_id = request.GET.get('company_id', 1)
resp = {'data': customer_information(company_id)}
return Response(data=resp, status=status.HTTP_200_OK)
This is url.py
url(r'^customer/$', CustomerInformationView.as_view()),
I'm having this problem. How can I solve this. Kindly guide me.
get function in your view should return responses.data, insted of responsed.
SIDE NOTE
First, let me point you to a resource that I think is GREAT for anything dealing with Django REST Framework:
Classy Django REST Framework. It is a fantastic resource because you can easily dig right into the source code to see how you may or may not need to override default operations.
MY ANSWER
What I suggest is that instead of using the APIView, you use ListAPIView.
It would look something like this:
from rest_framework.generics import ListAPIView
class Customer(models.Model):
name = models.CharField(max_length=200)
email_address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=20)
age = models.SmallIntegerField(default=14)
remarks = models.ManyToManyField(Remark,null=True,blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.id)
class Response(models.Model):
question = models.ForeignKey(Question)
customer = models.ForeignKey(Customer, related_name='responses')
response_text = models.CharField(max_length=100, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField()
def __str__(self):
return str(self.id)
class ResponseSerializer(ModelSerializer):
class Meta:
model = Response
fields = '__all__'
class RemarksSerializer(ModelSerializer):
class Meta:
model = Remark
fields = '__all__'
class CustomerInformationSerializer(ModelSerializer):
remarks = RemarksSerializer(many=True)
responses = ResponseSerializer(many=True)
class Meta:
model = Customer
fields = ('name', 'email_address', 'phone_number', 'age', 'remarks', 'responses')
class CustomerInformationView(ListAPIView):
queryset = Customer.objects.all()
serializer_class = CustomerInformationSerializer
lookup_field = 'remarks__company'
Note the change that I made by adding related_name to the customer field on your Response model. See Django documentation for more information on related_name. In short, it adds responses as a field name on your Customer model so that you can travel backwards through that relationship.
This is not tested, but this should be a better strategy to do what you want without having to have a get_responses method, or a services.py.
Some there might be error because of missing "/" at the end of path like "event-api"=incorrect and "event-api/" correct. That worked for me. Hope you also have same problem.
Incorrect: path('event-api',views.event_view,name="event-view")
Correct: path('event-api/',views.event_view,name="event-view")
I'm new on django, and I want to build an application called "Recycle Bank".
Here's my models.py
class Waste(models.Model):
name = models.CharField(max_length=20)
wtype = models.ForeignKey(WasteType)
specification = models.TextField(blank=True)
purchasing_price = models.DecimalField(max_digits=8, decimal_places=2)
selling_price = models.DecimalField(max_digits=8, decimal_places=2)
class Purchasing(models.Model):
timestamp = models.DateTimeField(auto_now=True)
client = models.ForeignKey(Client)
officer = models.ForeignKey(Officer)
waste = models.ManyToManyField(Waste, through='PurchasingDetail')
def __str__(self):
return self.client.user.username
class PurchasingDetail(models.Model):
waste = models.ForeignKey(Waste)
purchasing = models.ForeignKey(Purchasing)
weight = models.DecimalField(max_digits=8, decimal_places=2)
price = models.DecimalField(max_digits=8, decimal_places=2)
Here's my admin.py
class PurchasingDetailInlineAdmin(admin.TabularInline):
model = PurchasingDetail
extra = 1
class PurchasingAdmin(admin.ModelAdmin):
list_display = ['client', 'timestamp', 'officer',]
fields = ('client',)
inlines = [PurchasingDetailInlineAdmin,]
def save_model(self, request, obj, form, change):
obj.officer_id = request.user.id
obj.save()
So, I want to set the price object in PurchasingDetail(models.Model) with specified counting (in this case: Waste.purchasing_price * PurchasingDetail.weight). I know I have to get access to PurchasingDetail(models.Model) object like I do on my save_model(). How to do this?
Here's my admin interface screenshots https://drive.google.com/open?id=0B2dFcp9QmLL2X2duVHVfeVNHNlU
Thank you.