I have this model:
class Env(models.Model):
env_name = models.CharField(max_length=100, unique=True)
is_enabled = models.CharField(max_length=1, choices=ENABLED, default='Y')
def __unicode__(self):
return unicode(self.env_name)
I also have this model ...
class Hosts(models.Model):
host_name = models.CharField(max_length=200, unique=True)
host_variables = jsonfield.JSONField()
host_env = models.ForeignKey(Env, models.DO_NOTHING, related_name='host_env')
I wish to have a serialized representation equivalent to a join.
I'm trying to get rows that contain host_name and env_name
I can't seem to find the right way to serialize it
I'm have so far ...
class HostSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Hosts
fields = ('host_name', 'ip_address', 'is_enabled','is_managed','managed_users')
I can't seem to find the right way to get the name of the env in each row of my Hosts results.
What am I missing?
A serializer only handles a single Model, so anything else you want to add has to be added explicitly.
If you just want to add the env_name, you can use the SerializerMethodField field like this:
class HostSerializer(serializers.HyperlinkedModelSerializer):
env_name = serializers.SerializerMethodField()
class Meta:
model = Hosts
fields = ('host_name', 'env_name', 'ip_address', 'is_enabled','is_managed',
'managed_users',)
def get_env_name(self, obj):
host_env = obj.host_env
if host_env:
return str(host_env.env_name)
return None
Note that you may also want to look into nested serializers, but that would produce something like:
{
'host_name': 'my host name',
'host_env': {
'env_name': 'My env name'
}
}
See http://www.django-rest-framework.org/api-guide/relations/#nested-relationships for that (not explaining that as that was not your OP, but giving it to you as a reference for a potentially better way)
You can try
class HostSerializer(serializers.HyperlinkedModelSerializer):
env_name = serializers.ReadOnlyField(source='host_env.env_name')
class Meta:
model = Hosts
fields = ('host_name', 'ip_address', 'is_enabled','is_managed','managed_users', 'env_name',)
Related
I'm attempting to return JSON in a simple GET that includes a row from a joined table. The models:
class School(models.Model):
id = models.CharField(max_length=18,primary_key=True)
name = models.CharField(max_length=255,blank=False)
class Meta:
db_table='school'
class FKTable(models.Model):
school = models.ForeignKey(School,blank=False, db_column='school_id', on_delete=models.CASCADE)
name = models.CharField(max_length=45, blank=False)
value = models.CharField(max_length=255, blank=True, null=True)
class Meta:
db_table='fk_table'
And the serializer:
class SchoolListSerializer(serializers.ModelSerializer):
id = serializers.CharField(source='id')
name = serializers.CharField(source='name')
fkColumn = ????
class Meta:
model = models.School
fields = ('id','name','fkColumn')
The problem is I want to also filter the nested resource that I join against on the 'name' column so I don't know what to put for 'fkColumn' in the serializer. In SQL it would look something like this:
SELECT school.*, fk_table.value from school INNER JOIN fk_table on
fk_table.school_id = school.id AND fk_table.name = 'some name'
The end result JSON I want is:
{
"schools": [
{
"id": "someId",
"name": "someName",
"fkColumn": "Value"
}]}
RelatedField does not seem to work and none of the documentation seems to give a solid answer on how to do this. Any ideas?
Revised after clarification. Define an fkeySerializer:
class fkeySerializer(serializers.ModelSerializer):
...
Then link this in your main serializer:
fkey = fkeySerializer(many=True)
class Meta:
model = models.School
fields = ('id','name','fkColumn',)
You can use a MethodField serializer here. For example:
fkColumn = serializers.SerializerMethodField()
def get_fkColumn(self, obj):
return obj.fkColumn
class Meta:
model = models.School
fields = ('id','name','fkColumn',)
As an aside, perhaps there's a good reason for it, but it might be easier to rename this table to be more easily readable
I have the following models in Django:
class campaign(models.Model):
start_date = models.DateField('Start Date')
end_date = models.DateField('End Date')
description = models.CharField(max_length=500)
name = models.CharField(max_length=200)
active_start_time = models.TimeField()
active_end_time = models.TimeField()
last_updated = models.DateTimeField('Date updated',auto_now=True)
active = models.BooleanField(default=True)
client_id = models.ForeignKey('client',on_delete=models.PROTECT)
def __unicode__(self):
return u'%d | %s | %s' % (self.id,self.name, self.description)
class campaign_product(models.Model):
product_id = models.ForeignKey('product',on_delete=models.PROTECT)
last_updated = models.DateTimeField('Date updated',auto_now=True)
campaign_id = models.ForeignKey('campaign',on_delete=models.PROTECT)
class product(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=500)
sku = models.CharField(max_length=200,blank=True,null=True)
retail_price = models.DecimalField(decimal_places=2,max_digits=11)
discount_price = ((1,'Yes'),(0,'No'))
discounted_price = models.DecimalField(decimal_places=2,max_digits=11,blank=True,null=True)
category_id = models.ForeignKey('category',on_delete=models.PROTECT)
last_updated = models.DateTimeField('Date updated',auto_now=True)
active = models.BooleanField(default=True)
def __unicode__(self):
return u'%d | %s' % (self.id, self.name)
I also have the following serializer:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id')
And the following view set behavior in the urls.py file:
class campaignProductViewSet(viewsets.ModelViewSet):
queryset = campaign_product.objects.filter(campaign_id__start_date__lte=datetime.now(),campaign_id__end_date__gte=datetime.now(),campaign_id__active__exact=True)
serializer_class = campaignProductSerializer
My problem is I need to include the name field from the products model in my query results when for instance a request is made on http://127.0.0.1:8000/campaign_product/1/. Currenly this request returns only the product_id and the campaign_id. I tried making the serializer as follows:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id', 'product.name')
But then the service returns the following error:
Field name `product.name` is not valid for model `campaign_product`.
I event tried using product__name with and without quotes. Without quotes it tells me that there is no such variable, and with quotes it gives the is not valid for model error similar to the above. Heeelp! Getting this extra field is proving to be a pain :-(
What you want will need to look something more like this:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
product_name = serializers.CharField(source='product_id.name')
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id', 'product_name')
P.S. As an unrelated side note, it is generally a convention in Python code to name classes with CamelCase, such as Campaign, CampaignProduct, Product, and CampaignProductSerializer.
Edit: P.P.S. Originally, I had put written the product_name field with source='product.name'. This was actually due to me looking at the code too quickly and making assumptions based on Django conventions. Typically, with a Django ForeignKey, you would name the ForeignKey field after the model you are linking to, rather than explicitly naming it with _id. For example, the CampaignProduct model would typically be written with product = ForeignKey(...) and campaign = ForeignKey(...). In the background, Django will actually use product_id and campaign_id as the database field names. You also have access to those names on your model instances. But the product and campaign variables on your model instances actually return the objects which you are referring to. Hopefully that all makes sense.
I have a Class Course, and the model have a foreign key category
class Course(models.Model):
category = models.ForeignKey(Category)
title = models.CharField(max_length=128, unique=True)
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
I write the Serializer and View like:
class CourseSerializer(serializers.ModelSerializer):
class Meta:
model = Course
class CourseViewSet(ModelViewSet):
queryset = Course.objects.all()
serializer_class = CourseSerializer
when I make a get request, i get the response like:
{
'id':1,
'category':1,
'title': 'xxx'
}
but what i really want is:
{
'id':1,
'category_id':1,
'title': `xxx`
}
i want the key be category_id instead of category. i have tried to specified the name category_id in class Meta in CourseSerializer, but it doesn't work.
how can i make the code work as i want? thanks!
Edit:
also, when make the post request for creating a course instance, i want to post the data like:
{
'category_id': 1,
'title': 'xxx'
}
the key should also be category_id. how can i do this?
Try this,
note : indentation may not be correct here
class CourseSerializer(serializers.ModelSerializer):
category_id = serializers.SerializerMethodField()
class Meta:
model = Course
exclude = ['category']
def get_category_id(self, obj):
return obj.category.id
def create(self, validated_data):
return Course.objects.create(**validated_data)
It sounds like the SlugRelatedField may be what you need:
class CourseSerializer(serializers.ModelSerializer):
category_id = serializers.SlugRelatedField(slug_field='id',
queryset=Category.objects.all())
class Meta:
model = Course
fields = ('category_id',)
Let me know if that works!
I have got the answer, I should use PrimaryKeyRelatedField, and the serializer should be like:
class CourseSerializer(serializers.ModelSerializer):
category_id = serializers.PrimaryKeyRelatedField(source='category', queryset=Category.objects.all())
class Meta:
model = Course
I have several a base model with several control fields. Among them a location fields compound from lat, lon, accuracy, provider and client time. Most of my writable models (and hence resources) are inheriting from this base model.
I'm trying to make DRF serialize the location related fields in a nested "location" field. For example,
{
"id": 1,
"name": "Some name",
"location": {
"lat": 35.234234,
"lon": 35.234234,
"provider": "network",
"accuracy": 9.4,
}
}
I'ts important to remember that these fields are regular (flat) fields on the base model.
I've investigated and found several options
Create a custom field and by overriding "get_attribute" create the nested representation. I don't like this solution because i lose some of the benefits of the model serializer such as validation.
Create a nested resource called Location. I guess i could make it work by adding a property by the same name on the model but again, no validations.
So my question is, What is the best way to nest ( or group ) several fields in a DRF serializer ?
DRF 3.0.0, Django 1.7
EDIT:
Building on top of #Tom Christie answer this is what i came up with (simplified)
# models.py
class BaseModel(models.Model):
id = models.AutoField(primary_key=True)
lat = models.FloatField(blank=True, null=True)
lon = models.FloatField(blank=True, null=True)
location_time = models.DateTimeField(blank=True, null=True)
location_accuracy = models.FloatField(blank=True, null=True)
location_provider = models.CharField(max_length=50, blank=True, null=True)
#property
def location(self):
return {
'lat': self.lat,
'lon': self.lon,
'location_time': self.location_time,
'location_accuracy': self.location_accuracy,
'location_provider': self.location_provider
}
class ChildModel(BaseModel):
name = models.CharField(max_lengtg=10)
# serializers.py
class LocationSerializer(serializers.Serializer):
lat = serializers.FloatField(allow_null=True, required=False)
lon = serializers.FloatField(allow_null=True, required=False)
location_time = serializers.DateTimeField(allow_null=True, required=False)
location_accuracy = serializers.FloatField(allow_null=True, required=False)
location_provider = serializers.CharField(max_length=50,allow_null=True, required=False)
class BaseSerializer(serializers.ModelSerializer):
def create(self,validated_data):
validated_data.update(validated_data.pop('location',{}))
return super(BaseSerializer,self).create(validated_data)
def update(self, instance, validated_data):
location = LocationSerializer(data=validated_data.pop('location',{}), partial=True)
if location.is_valid():
for attr,value in location.validated_data.iteritems():
setattr(instance,attr,value)
return super(BaseSerializer,self).update(instance, validated_data)
class ChildSerializer(BaseSerializer):
location = LocationSerializer()
class meta:
model = ChildModel
fields = ('name','location',)
I've tested with valid/invalid post/patch and it worked perfectly.
Thanks.
I'd suggest simply using explicit serializer classes, and writing the fields explicitly. It's a bit more verbose, but it's simple, obvious and maintainable.
class LocationSerializer(serializers.Serializer):
lat = serializers.FloatField()
lon = serializers.FloatField()
provider = serializers.CharField(max_length=100)
accuracy = serializers.DecimalField(max_digits=3, decimal_places=1)
class FeatureSerializer(serializers.Serializer):
name = serializers.CharField(max_length=100)
location = LocationSerializer()
def create(self, validated_data):
return Feature.objects.create(
name=validated_data['name'],
lat=validated_data['location']['lat'],
lon=validated_data['location']['lat'],
provider=validated_data['location']['provider'],
accuracy=validated_data['location']['accuracy']
)
def update(self, instance, validated_data):
instance.name = validated_data['name']
instance.lat = validated_data['location']['lat']
instance.lon = validated_data['location']['lat']
instance.provider = validated_data['location']['provider']
instance.accuracy = validated_data['location']['accuracy']
instance.save()
return instance
There's a bunch of ways you could use a ModelSerializer instead, or ways to keep the create and update methods a little shorter, but it's not clear that the extra indirection you'd be giving yourself is at all worth it.
We almost always use completely explicit serializer classes for APIs that we're building.
I have a problem with django and tastypie
Given is the following code:
class CandidatePollResource(ModelResource):
class Meta:
queryset = Candidate.objects.all()
resource_name = "candidate-poll"
filtering = {"status": ALL }
class Candidate(Profile):
"""
This profile stores all information about a candidate.
"""
status = models.CharField(_('status'), max_length=15, blank=True, choices=CANDIDATE_STATUS_CHOICES)
class Profile(models.Model):
"""
Abstract basic class all profiles should inherit.
"""
user = models.OneToOneField(CustomUser,related_name='%(class)s',)
invitationdate = models.DateField(_('invitationdate'), null=True, blank=True)
confirmationdate = models.DateField(_('confirmationdate'), null=True, blank=True)
activation_key = models.CharField(_('activation_key'), max_length=32, blank=True)
# Adding of "class" here is important for this to work. See
# http://thedjangoforum.com/board/thread/417/reverse-query-name-for-field-clash/?page=1
created_by = models.ForeignKey(CustomUser, related_name='%(class)s_created', blank=True, null=True)
objects = ProfileManager()
class Meta:
abstract = True
Each time we try to do a call to filter the result set ( http://localhost:3000/api/v1/candidate-poll/?status__exact=new ), I always get the following error
The 'status' field does not allow filtering.
How can I enable filtering on that field?
I think that your syntax is not exactly correct.
Instead of:
filtering = {"status": ("exact", "in",), }
try:
filtering = {"status": [ "exact", "in" ] }
if that doesn't work, you could try:
filtering = {"status": ALL }
and proceed from there. ALL should allow everything so if it didn't work this would mean the problem lies elsewhere.
For more information please take a look at Tastypie docs
go with
filtering = { 'status' : ['exact'] }
now you can use in URL http://localhost:3000/api/v1/candidate-poll/?status=new and the field gets filtered with only json data having status as new