How to serialize relationship in django rest? - json

Try to serialize this Models
Model:
class Order (models.Model):
id = models.AutoField(primary_key=True)
date_create = models.DateField(auto_now_add=True)
date_change = models.DateField(auto_now=True)
summ =models.CharField(max_length=15,default='0')
delivery = models.ForeignKey('Delivery')
success = models.BooleanField(default=False)
paymentMethod = models.ForeignKey('Payments')
def __unicode__(self):
return unicode(self.id)
class OrderProduct(models.Model):
order=models.ForeignKey('Order')
id = models.AutoField(primary_key=True)
date_create = models.DateField(auto_now_add=True)
date_change = models.DateField(auto_now=True)
price = models.IntegerField()
product = models.ForeignKey('product.Product')
additionals = models.IntegerField(null=True,default=0)
count = models.IntegerField()
def __unicode__(self):
return self.id
class Delivery(models.Model):
id = models.AutoField(primary_key=True)
date_create = models.DateField(auto_now_add=True)
date_change = models.DateField(auto_now=True)
delivery_time = models.DateTimeField()
delivery_adress = models.TextField()
phone = models.TextField()
def __unicode__(self):
return self.phone
class Payments(models.Model):
id = models.AutoField(primary_key=True)
date_create = models.DateField(auto_now_add=True)
date_change = models.DateField(auto_now=True)
title = models.TextField();
def __unicode__(self):
return self.title
Serializers:
class DeliverySerializer(serializers.ModelSerializer):
class Meta:
model = Delivery
fields = ('id', 'delivery_time','delivery_adress','phone')
def create(self, validated_data):
return Delivery.objects.create(**validated_data)
class PaymentsSerializer(serializers.ModelSerializer):
class Meta:
model = Payments
fields = ('id', 'title')
def create(self, validated_data):
return Payments.objects.create(**validated_data)
class OrderSerializer(serializers.ModelSerializer):
delivery = DeliverySerializer(read_only=True)
paymentMethod = PaymentsSerializer(read_only=True)
class Meta:
model = Order
fields = ('id', 'delivery', 'paymentMethod','summ','success')
def create(self, validated_data):
deliverys_data = validated_data.pop('delivery')
paymentsMethod_data = validated_data.pop('paymentMethod')
order = Order.objects.create(**validated_data)
for delivery_data in deliverys_data:
Delivery.objects.create(order=order, **delivery_data)
for paymentMethod_data in paymentsMethod_data:
Payments.objects.create(order=order, **paymentMethod_data)
return order
View:
#api_view(['POST'])
def order_post(request, format=None):
#List all snippets, or create a new snippet.
if request.method == 'POST':
serializer = OrderSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I need to get data by 1 packet, and then write data in DB
EveryTime I get a error:
deliverys_data = validated_data.pop('delivery')
KeyError: 'delivery'
Example of JSON packet
[{"delivery":{"delivery_time":"2016-05-31T12:18:47Z","delivery_adress":"123","phone":"123"},"paymentMethod":{"id":1,"title":"123124123"},"summ":"23","success":false}]

You are getting KeyError: 'delivery' because you have set delivery field as read_only. If DRF finds this field in the input, it will ignore that field.
From docs on read_only argument:
Read-only fields are included in the API output, but should not be
included in the input during create or update operations. Any
'read_only' fields that are incorrectly included in the serializer
input will be ignored.
Also, since you are using paymentMethod field in the create() method, you need to tell DRF to consider that field also in the input.
So, you need to remove the read_only argument from your serializer for both delivery and paymentMethod fields so that these fields are considered when deserializing.
class OrderSerializer(serializers.ModelSerializer):
delivery = DeliverySerializer() # remove read_only argument
paymentMethod = PaymentsSerializer() # remove read_only argument
Secondly, you are sending the data incorrectly. You need to send a single order input instead of list of orders you are sending.
# send without the list
{"delivery":{"delivery_time":"2016-05-31T12:18:47Z","delivery_adress":"123","phone":"123"},"paymentMethod":{"id":1,"title":"123124123"},"summ":"23","success":false}

EveryTime I get a error: deliverys_data = validated_data.pop('delivery') KeyError: 'delivery'
This is to be expected since the OrderSerialzier has DeliverySerializer flagged as read_only.
If you want to get the data writable, you'll need to remove that flag first.

Related

How do I select only the fields i wanted from multiple Models in Django rest framework?

This is my models.py:
class UnitOfMeasurement(models.Model):
active = models.BooleanField(default=True)
name = models.CharField(max_length=255)
measurement_type = models.CharField(max_length=255)
def __str__(self):
return self.name
class Category(models.Model):
name = models.CharField(max_length=250)
abbreviation=models.CharField(max_length=10)
active = models.BooleanField(default=True)
description=models.CharField(max_length=500,blank=True, null=True)
def __str__(self):
return self.name
class Product(models.Model):
active = models.BooleanField(default=True)
bar_code = models.CharField(max_length=50, blank = True, null = True)
name = models.CharField(max_length=250)
category = models.ForeignKey(
Category, on_delete=models.CASCADE, related_name='product')
brand = models.CharField(max_length=250)
model = models.CharField(max_length=250)
tag = models.CharField(max_length=250, null=True)
remarks = models.TextField(null=True)
gstcode = models.ForeignKey(
GSTCode, on_delete=models.CASCADE, related_name='product', null=True)
unit_of_measurement = models.ForeignKey(
UnitOfMeasurement, on_delete=models.DO_NOTHING, related_name='category')
# picture
objects=ProductManager()
# select the product where active=true & quantity<0
def __str__(self):
return self.name
So how do I need to code my serializers and views in order to get the data that returns name, category, brand, model, tag from Product Model and name from Category Model and also name and measurement_type from UnitOfMeasurement Model?
You can select fields from model in serializer as below example code for UnitOfMeasurement and use this serializer for get(retrieve) request.
class UnitOfMeasurementSerializer(serializers.ModelSerializer):
class Meta:
model = UnitOfMeasurement
fields = ["name", "measurement_type"]
Same thing you can do for rest of models.
Views depends on your use case.

Left Outer Join 2 models and return query in JSON

I am using DRF to serialize and have the api endpoints to display json data.
I have 2 models -
class FooOne(models.Model):
field_a = models.CharField(max_length=255, null=False)
field_b = models.CharField(max_length=255, null=False)
field_c = models.CharField(max_length=255, null=False)
class FooTwo(models.Model):
field_fk = models.ForeignKey(FooOne)
field_d = models.CharField(max_length=255, null=False)
field_e = models.CharField(max_length=255, null=False)
these are my serializer classes -
class FooOneSerializer(serializers.ModelSerializer):
class Meta:
model = FooOne
fields = (field_a, field_b, field_c)
class FooTwoSerializer(serializers.ModelSerializer):
field_fk = FooOneSerializer()
class Meta:
model = FooTwo
fields = (field_fk, field_d, field_e)
FooTwoSerializer will return data with field_fk as a nested dictionary. What i want to is to return other fields of FooOneSerialzer that arent foreign key to FooTwo and append it the final rendered result of FooTwoSerializer.
How do i go about this?
If I understand well, you want concatenate the results of FooOneSerailizer and all fields of FooTwoSerializer. You can do so by overriding to_represetation() method.
class FooTwoSerializer(serializers.ModelSerializer):
class Meta:
model = FooTwo
fields = (field_d, field_e)
def to_representation(self, instance):
# get OrderedDict with original fields
response = super(FooTwoSerializer, self).to_representation(instance)
# get OrderedDict with foreign foo_one data
foo_one = FooOneSerializer(instance.field_fk).data
# set this fields to original data
for key in foo_one:
response[key] = foo_one[key]
return response

Django, how to make a query with [ORDER BY CONVERT (name USING gbk) ASC]

I want to make a query with [ORDER BY CONVERT (name USING gbk) ASC] ,so that foreignkey list in admin change/add form can be sorted by Chinese Pinyin , is it possible?
DB: mysql ,
Sample class are blow (Character sets of table commoninfo : utf8_general_ci):
class CommonInfo(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Personal(CommonInfo):
first_name = models.CharField(max_length=128,null=True, blank=True, default = None)
last_name = models.CharField(max_length=128,null=True, blank=True, default = None)
class Profile(models.Model):
personal_info = models.ForeignKey(Personal, null=True, blank=True, default = None)
mobile = models.CharField(max_length=32,default='',null=True, blank=True)
I tried below 2 methods in ProfileAdmin but neither works
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "personal_info":
#raw_sql = 'SELECT id as commoninfo_ptr_id, name FROM share_commoninfo ORDER BY CONVERT (name USING gbk) ASC'
#raw_querySet = Personal.objects.raw(raw_sql)
#kwargs["queryset"] = raw_querySet
querySet = Personal.objects.extra(order_by=['CONVERT (name USING gbk)'])
kwargs["queryset"] = querySet
return super(ProfileAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
admin.site.register(models.Profile, ProfileAdmin)
FieldError: Cannot resolve keyword 'CONVERT (name USING gbk)' into
field. Choices are: commoninfo_ptr, commoninfo_ptr_id, first_name, id,
last_name, name, profile, Check fields/fieldsets/exclude attributes of
class ProfileAdmin.
from django.db.models import Func, Value
class Convert(Func):
def __init__(self, expression, transcoding_name, **extra):
super(Convert, self).__init__(
expression, transcoding_name=Value(transcoding_name), **extra)
def as_mysql(self, compiler, connection):
self.function = 'CONVERT'
self.template = '%(function)s(%(expressions)s AS %(transcoding_name)s)'
return super(Conver, self).as_sql(compiler, connection)
Usage:
queryset.order_by(Convert('name', 'gbk').asc())
Documentation:
Func() expressions
Examples of database functions
you can use extra
queryset.extra(
select={'convert_name': 'convert(name using gbk)'},
order_by=['convert_name']
)

how to add default related record with sqlaclhemy (e.g. user belongs to group)

I have two models: AuthUser and AuthGroup, they are linked via a many2many relationship. By default I have at least 3 user "states":
unlogged -> no group
logged in -> users group
admin -> admin group
I would like to be sure that whenever a new user is added to the database, it is added to the users group too. Is there a way to get this functionality in the model defintion?
here are my table definitions
Base = declarative_base()
user_group_table = Table('auth_user_groups', Base.metadata,
Column('user_id', types.Integer(), \
ForeignKey('auth_users.id', onupdate='CASCADE', ondelete='CASCADE')),
Column('group_id', types.Integer(), \
ForeignKey('auth_groups.id', onupdate='CASCADE', ondelete='CASCADE'))
)
class AuthGroup(Base):
__tablename__ = 'auth_groups'
__table_args__ = {"sqlite_autoincrement": True}
id = Column(types.Integer(), primary_key=True)
name = Column(Unicode(80), unique=True, nullable=False)
created = Column(types.DateTime(), default=functions.now())
users = relation('AuthUser', secondary=user_group_table, \
backref='auth_groups')
def __repr__(self):
return u'%s' % self.name
def __unicode__(self):
return self.name
class AuthUser(Base):
__tablename__ = 'auth_users'
__table_args__ = {"sqlite_autoincrement": True}
id = Column(types.Integer(), primary_key=True)
login = Column(Unicode(80), default=u'', index=True)
username = Column(Unicode(80), default=u'', index=True)
_password = Column('password', Unicode(80), default=u'', index=True)
email = Column(Unicode(80), default=u'', index=True)
groups = relation('AuthGroup', secondary=user_group_table, \
backref='auth_users')
def _set_password(self, password):
self._password = bcrypt.hashpw(password, bcrypt.gensalt())
def _get_password(self):
return self._password
password = synonym('_password', descriptor=property(_get_password, \
_set_password))
#classmethod
def get_by_id(cls, id):
return DBSession.query(cls).filter(cls.id==id).first()
#classmethod
def get_by_login(cls, login):
return DBSession.query(cls).filter(cls.login==login).first()
#classmethod
def get_by_username(cls, username):
return DBSession.query(cls).filter(cls.username==username).first()
#classmethod
def get_by_email(cls, email):
return DBSession.query(cls).filter(cls.email==email).first()
#classmethod
def check_password(cls, **kwargs):
if kwargs.has_key('id'):
user = cls.get_by_id(kwargs['id'])
if kwargs.has_key('username'):
user = cls.get_by_username(kwargs['username'])
if not user:
return False
if bcrypt.hashpw(kwargs['password'], user.password) == user.password:
return True
else:
return False
usually a class constructor validates expected data upon new object creation (an INSERT in the ORM, short of any trickery to bypass the usual mechanics, always corresponds to a new instance of a mapped class created via constructor):
class AuthUser(Base):
def __init__(self, **kw):
self.groups = kw.pop('groups', None)
if not self.groups:
raise ValueError("at least one group is required!")
super(AuthUser, self).__init__(**kw)

how to model the following in Django correctly

I have the following tables. The ComponentBuild table has two columns 'comp_pn' and 'parent_pn' that are both ForeignKeys. Django is complaining Unknown column 'product_parent_pn.product_id_id. What's the correct way to model these relationship?
class Product(models.Model):
name = models.CharField(max_length=80)
def __unicode__(self):
return self.name
class ProductParentPn(models.Model):
part_no = models.CharField(max_length=80)
product_id = models.ForeignKey(Product)
def __unicode__(self):
return self.part_no
class CompMap(models.Model):
component = models.CharField(max_length=50)
part_no = models.CharField(max_length=50)
def __unicode__(self):
return self.part_no
class CompProductMap(models.Model):
comp_id = models.IntegerField()
prod_id = models.IntegerField()
class Meta:
db_table = u'comp_product_map'
class ComponentBuild(models.Model):
comp_sn = models.CharField(max_length=35)
parent_sn = models.CharField(max_length=15)
comp_pn = models.ForeignKey(CompMap)
parent_pn = models.ForeignKey(ProductParentPn)
date = models.DateField(default=datetime.datetime.today())
def __unicode__(self):
return self.comp_sn