django serializer with nested column - django

I'm calling the following serializer -
class ResourceSerializer(serializers.ModelSerializer):
class Meta:
model = Resmst
resource_name = 'resmst'
fields = ('resmst_id', 'resmst_name', 'resmst_desc', 'resmst_limit', 'resmst_inuse', 'resmst_active', 'resmst_lstchgtm',
'resmst_prntid', 'resmst_owner', 'resmst_public', 'resmst_locked', 'resmst_offline')
read_only_fields = ('resmst_id',)
resmst_owner is a FK relation to another table. What I want to do is have the serializer display the information from a column where that FK relates.
This is the current json -
[
{
"resmst_id": 204,
"resmst_name": "GAWK",
"resmst_desc": null,
"resmst_limit": 1,
"resmst_inuse": 0,
"resmst_active": "Y",
"resmst_lstchgtm": "2014-08-20T11:15:18",
"resmst_prntid": null,
"resmst_owner": 822,
"resmst_public": "Y",
"resmst_locked": null,
"resmst_offline": 0
}
]
And this is how I want it to look -
[
{
"resmst_id": 204,
"resmst_name": "GAWK",
"resmst_desc": null,
"resmst_limit": 1,
"resmst_inuse": 0,
"resmst_active": "Y",
"resmst_lstchgtm": "2014-08-20T11:15:18",
"resmst_prntid": null,
"owner_name": "John Smith",
"resmst_public": "Y",
"resmst_locked": null,
"resmst_offline": 0
}
]
Or am I stuck with having to do it this way -
[
{
"resmst_id": 204,
"resmst_name": "GAWK",
"resmst_desc": null,
"resmst_limit": 1,
"resmst_inuse": 0,
"resmst_active": "Y",
"resmst_lstchgtm": "2014-08-20T11:15:18",
"resmst_prntid": null,
"resmst_owner": {
"owner_name": "John Smith"
},
"resmst_public": "Y",
"resmst_locked": null,
"resmst_offline": 0
}
]

Haven't tested this but it looks like this question. Try to use a SerializerMethodField.

I think you should be fine with a regular Field (note that this is readonly).
class ResourceSerializer(serializers.ModelSerializer):
owner_name = serializers.Field(source='resmst_owner.owner_name')
class Meta:
model = Resmst
resource_name = 'resmst'
fields = ('resmst_id', 'resmst_name', 'resmst_desc', 'resmst_limit',
'resmst_inuse', 'resmst_active', 'resmst_lstchgtm',
'resmst_prntid', 'resmst_owner', 'resmst_public',
'resmst_locked', 'resmst_offline', 'owner_name', )
read_only_fields = ('resmst_id',)
Don't forget to add it to Meta.fields (as above).
See the section on Core arguments:
source
The name of the attribute that will be used to populate the
field. May be a method that only takes a self argument, such as
Field(source='get_absolute_url'), or may use dotted notation to
traverse attributes, such as Field(source='user.email').
The value source='*' has a special meaning, and is used to indicate
that the entire object should be passed through to the field. This can
be useful for creating nested representations. (See the implementation
of the PaginationSerializer class for an example.)
Defaults to the name of the field.

Related

Django annotate several same objects in QuerySet by different related object

I got:
# models
class Building(models.Model):
...
class Flat(models.Model):
building = models.ForeignKey(Building)
class Profile(models.Model):
flats = models.ManyToManyField(Flat)
# logic
building = Building.objects.create()
flat_1 = Flat.objects.create(building=building)
flat_2 = Flat.objects.create(building=building)
profile = Profile.objects.create()
profile.flats.add(flat_1)
profile.flats.add(flat_2)
profiles = Profile.objects.filter(flats__building=building)
I got in profiles 2 same profiles. How i can annotate each of them by different flat like this: profiles.first().flat == flat_1 and profiles.last().flat == flat_2?
Maybe Subquery() but how?
UPD I need this in some DRF list view. Output in JSON must be something like:
[
{
"profile_id": 1,
"flat_id": 2
},
{
"profile_id": 1,
"flat_id": 3
}
]
To obtain that output, you could do:
data = Profile.objects.all().values('flats', 'id')
return Response(data=data)
in your DRF view.
You don't have to profile instances ...
I wrote the code for your exact needs at the end, but first wrote a couple of things that might be of interest.
In your code sample, you've created only one profile, I'm sure you are not getting 2 instances of Profile that are equals but only one.
The thing is if you have a QuerySet with only one entry, then:
profiles.first() == profiles.last() # True
since profile.first() and profiles.last() are the same instance.
You should try creating 2 Profile instances:
building = Building.objects.create()
flat_1 = Flat.objects.create(building=building)
flat_2 = Flat.objects.create(building=building)
profile_1 = Profile.objects.create() # You coud/should use bulk_create here.
profile_2 = Profile.objects.create()
profile_1.flats.add(flat_1)
profile_2.flats.add(flat_2)
Then
profiles = Profile.objects.filter(flats__building=building)
will return two different profile objects.
On the other hand, obtaining the JSON like you want ...
Following the example, you posted, filter flats by profile and get the values (this also works if you have more that one profile).
Flat.objects.filter(profile=profile_1).values('profile__id', 'id')
This will return something like ("id" stands for flats ids):
[
{
"profile__id": 1,
"id": 1
},
{
"profile__id": 1,
"id": 3
}
]
If you do not filter by profile (and you have more than one) you could get something like:
[
{
"profile__id": 1,
"id": 1
},
{
"profile__id": 2,
"id": 3
},
{
"profile__id": 2,
"id": 4
},
...
]
Annotating to get the EXACT json you want:
Filter as shown previously annotate, and get desired values:
Flat.objects.filter(profile=profile_1).annotate(
flat_id=F('id')
).annotate(
profile_id=F('profile__id')
).values(
'profile_id', 'flat_id'
)
will give exactly what you want:
[
{
"profile_id": 1,
"flat_id": 2
},
{
"profile_id": 1,
"flat_id": 3
}
]
You can do that with the right serializer and the right annotation:
The serializer:
class FlatSerializer(serializers.ModelSerializer):
class Meta:
model = Flat
fields = ('flat_id', 'building_id')
flat_id = serializers.CharField(read_only=True)
Then I would simply query Flats rather than profiles and serialize:
flats = Flat.objects \
.annotate(flat_id=F('id')) \
.filter(building=building)
serialized = FlatSerializer(flats, many=True)
print(serialized.data) # [ { flat_id: 1, building_id: 1 }, { flat_id: 2, building_id: 1 } ]
Let me know if that works for you

Django rest api framework ModelSerializer

I have ModelSerrializer:
class ProbeParamsSerializer(serializers.ModelSerializer):
modems = ModemParamsSerializer(many=True, required=True)
net_adapters = NetworkAdapterParamsSerializer(many=True, required=True)
class Meta:
model = ProbeParams
fields = (
'probe', 'modems', 'net_adapters', 'ram_free', 'sd_free', 'num_measurement_files', 'num_audio_files',
'cpu_t',
'cpu_load', 'latitude', 'longitude', 'navigation_status', 'info_updated')
I perform:
probe_params_serializer = ProbeParamsSerializer(data=item)
Item look as:
{
"info_updated": 56483476,
"config_updated": 325687,
"ram_free": 800,
"sd_free": 2000,
"num_measurement_files": 8,
"num_audio_files": 2,
"cpu_temp": 38.7,
"cpu_load": 58.4,
"latitude": 0,
"longitude": 0,
"modems": [
Note parameter: cpu_temp. The model is called сpu_t and the report comes under the same name cpu_temp. So naturally when performing validation, I get an error -
"this field (сpu_t) is required".
I understand which is why there is an error, but I do not know how to link these two names. It is necessary to make the value of the parameter cpu_temp are stored in a parameter cpu_t.
Help me please.

in tastypie, how can i set name for json result

In tastypie, I want set json result name.
I have a class that I use for it but I can set name in.
enter cclass ContentResource(ModelResource):
class Meta:
results = ListField(attribute='results')
queryset = Content.objects.all()
resource_name = 'content'
max_limit = None
#filtering = {"title": "contains"}
def alter_list_data_to_serialize(self, request, data_dict):
if isinstance(data_dict, dict):
if 'meta' in data_dict:
# Get rid of the "meta".
del(data_dict['meta'])
# Rename the objects.
data_dict['Mobile'] = data_dict['objects']
del(data_dict['objects'])
return data_dict
ode here it returns this
{"Mobile":
[
{
"added": "2015-07-23T11:30:20.911835",
"content_cast": "",
"content_company": "HamrahCinema",
"content_description": "so nice",
"content_director": "",
"content_duration": "2:20",
"content_filelanguage": null,
}
]
}
when I use /content/api/content every thing is ok, but when I use /content/api/content/1,"mobile" is removed.
as educated guess, I would suggest using alter_detail_data_to_serialize

how to exclude django hstore field in admin?

The standard approach in admin.py by defining class:
exclude = ('some_hstore_field', )
does not work.
I manage to get expected result by specifying explicit fields but would like rather exclude the one I don't need, than specify all the other needed.
Assuming you have in your model hstore field called facilities:
facilities = hstore.DictionaryField(schema=HSTORE_SCHEMA)
then you CAN NOT just write:
exclude = ('some_non_hstore_field', 'facilities')
Assuming your hstore schema looks something like that:
HSTORE_SCHEMA = [
{
"name": "vegetarian_menu",
"class": "BooleanField",
"kwargs": {
"default": False,
"verbose_name": "vegetarian menu"
}
},
{
"name": "vegan_menu",
"class": "BooleanField",
"kwargs": {
"default": False,
"verbose_name": "vegan menu"
}
}
]
You have to exclude each of subfield by its name, e.g:
exclude = ('some_non_hstore_field', 'vegetarian_menu', 'vegan_menu')
you can do it like this:
exclude = tuple(['some_non_hstore_field'] + [field['name'] for field in HSTORE_SCHEMA])
or like this - using meta of class field:
exclude = tuple(['some_non_hstore_field'] + [field['name'] for field in YourModel._meta.get_field_by_name('facilities')[0].schema])
The same applies for readonly_fields

django rest framework - get two models

I have multiple models that are associated by Foreign Keys. I can export them all separately using django rest framework on a one to one basis, and I can also export multiple ones nested. However I want to be able to essentially "concatenate" them together into a single json/xml export.
The models in the example below are joined by a one to one foreign key on jobdtl_id. I have some where it's one to many but I'm hoping I can figure that out when I know how to get a view that will link 2 separate models like I want below -
Here's an example of what I want the json to look like by hitting a single URL like
http://localhost/job/4/
{
"job": {
"-id": "9878",
"-name": "This is the job",
"-master": "blahserver",
"-dbversion": "234",
"-xmlversion": "1",
"jobmst": {
"jobmst_id": "9878",
"jobmst_type": "2",
"jobmst_prntid": "234",
"jobmst_active": "Y",
"jobmst_name": "This is the job",
"jobmst_owner": "Owner",
"jobdtl_id": "9878",
"jobmst_lstchgtm": {
"-date": "Y",
"#text": "2013-10-23 09:22:08.0"
},
"jobmst_prntname": "Parent",
"jobmst_alias": "9878"
},
"jobdtl": {
"jobdtl_id": "9878",
"jobdtl_cmd": "blah.exe",
"jobdtl_failalarm": "NULL",
"nodmst_id": "NULL",
"nodlstmst_id": "NULL",
"jobdtl_inhevent": "Y",
"jobdtl_inhoptions": "Y",
"jobdtl_inhagent": "Y",
"jobdtl_inhrepeat": "Y",
"jobdtl_inhtime": "Y",
"jobdtl_timewin": "NULL",
"jobdtl_saveoutput": "Y",
"jobdtl_outputname": "NULL",
"jobdtl_trackmethod": "1",
"jobdtl_trackcmd": "NULL",
"jobdtl_deplogic": "1",
"jobdtl_rerun": "NULL",
"jobdtl_params": "--blah --ok"
},
"jobdep": [
{
"jobdep_id": "79670",
"jobmst_id": "9878",
"jobdep_type": "1",
"jobdep_jobmst": "another job",
"varmst_id": "NULL"
},
{
"-num": "2",
"jobdep_id": "83783",
"jobmst_id": "9878",
"jobdep_type": "1",
"jobdep_jobmst": "and another",
"varmst_id": "NULL"
}
],
"trgjob": [
{
"trgjob_id": "22286",
"trgmst_id": "23455",
"jobmst_id": "9878"
},
{
"-num": "2",
"trgjob_id": "28980",
"trgmst_id": "23521",
"jobmst_id": "9878"
},
{
"-num": "3",
"trgjob_id": "28981",
"trgmst_id": "9237",
"jobmst_id": "9878"
}
]
}
}
The models are basically like this -
class Jobdtl(models.Model):
jobdtl_id = models.IntegerField(primary_key=True)
jobdtl_cmd = models.TextField(blank=True)
....
jobdtl_duration = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'jobdtl'
class Jobmst(models.Model):
jobmst_id = models.IntegerField(primary_key=True)
jobmst_type = models.SmallIntegerField()
....
jobdtl_id = models.ForeignKey('Jobdtl', db_column='jobdtl_id', related_name='mstdtl', blank=True, null=True)
def __unicode__(self):
return self.jobmst_name
class Meta:
managed = False
db_table = 'jobmst'
end caveat I'm converting the json from how the XML looks for the existing legacy app which is like so -
<?xml version="1.0"?>
<job id="9878" name="This is the job" master="blahserver" dbversion="532" xmlversion="1">
<jobmst>
<jobmst_id>9878</jobmst_id>
<jobmst_type>2</jobmst_type>
<jobmst_prntid>234</jobmst_prntid>
<jobmst_active>Y</jobmst_active>
<jobmst_name>This is the job</jobmst_name>
<jobmst_owner>Owner</jobmst_owner>
<jobdtl_id>9878</jobdtl_id>
<jobmst_lstchgtm date="Y">2013-10-23 09:22:08.0</jobmst_lstchgtm>
<jobmst_prntname>Parent</jobmst_prntname>
<jobmst_alias>9878</jobmst_alias>
</jobmst>
<jobdtl>
<jobdtl_id>9878</jobdtl_id>
<jobdtl_cmd>blah.exe</jobdtl_cmd>
<jobdtl_failalarm>NULL</jobdtl_failalarm>
<nodmst_id>NULL</nodmst_id>
<nodlstmst_id>NULL</nodlstmst_id>
<jobdtl_inhevent>Y</jobdtl_inhevent>
<jobdtl_inhoptions>Y</jobdtl_inhoptions>
<jobdtl_inhagent>Y</jobdtl_inhagent>
<jobdtl_inhrepeat>Y</jobdtl_inhrepeat>
<jobdtl_inhtime>Y</jobdtl_inhtime>
<jobdtl_timewin>NULL</jobdtl_timewin>
<jobdtl_saveoutput>Y</jobdtl_saveoutput>
<jobdtl_outputname>NULL</jobdtl_outputname>
<jobdtl_trackmethod>1</jobdtl_trackmethod>
<jobdtl_trackcmd>NULL</jobdtl_trackcmd>
<jobdtl_deplogic>1</jobdtl_deplogic>
<jobdtl_rerun>NULL</jobdtl_rerun>
<jobdtl_params>--blah --ok</jobdtl_params>
</jobdtl>
<jobdep>
<jobdep_id>79670</jobdep_id>
<jobmst_id>9878</jobmst_id>
<jobdep_type>1</jobdep_type>
<jobdep_jobmst>another job</jobdep_jobmst>
<varmst_id>NULL</varmst_id>
</jobdep>
<jobdep num="2">
<jobdep_id>83783</jobdep_id>
<jobmst_id>9878</jobmst_id>
<jobdep_type>1</jobdep_type>
<jobdep_jobmst>and another</jobdep_jobmst>
<varmst_id>NULL</varmst_id>
</jobdep>
<trgjob>
<trgjob_id>22286</trgjob_id>
<trgmst_id>23455</trgmst_id>
<jobmst_id>9878</jobmst_id>
</trgjob>
<trgjob num="2">
<trgjob_id>28980</trgjob_id>
<trgmst_id>23521</trgmst_id>
<jobmst_id>9878</jobmst_id>
</trgjob>
<trgjob num="3">
<trgjob_id>28981</trgjob_id>
<trgmst_id>9237</trgmst_id>
<jobmst_id>9878</jobmst_id>
</trgjob>
</job>
class MSTSerializer(serializers.HyperlinkedModelSerializer):
jobdtl_id = DTLSerializer()
class Meta:
model = Jobmst
fields = ('id', 'url', 'jobdtl_id'...)
class DTLSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Jobdtl
fields = ('id', 'url', ...)
Would result in a more correct data structure of
{
"jobmst_id": 4,
"jobmst_type": 1,
"jobdtl_id": {
"jobdtl_id": 4,
"jobdtl_cmd": null,
"jobdtl_duration": 1379
},
}
I found the solution by doing the following in my views.py
...
#csrf_exempt
def tesxml_test(request, pk):
"""
Retrieve, update or delete a code snippet.
"""
try:
mst = Jobmst.objects.using('database1').get(jobmst_id=pk)
dtl = Jobdtl.objects.using('database1').get(jobdtl_id=pk)
dep = Jobdep.objects.using('database2').filter(jobmst_id=pk).order_by('jobdep_id')
trg = Trgjob.objects.using('database1').filter(jobmst_id=pk).order_by('trgjob_order')
except Jobmst.DoesNotExist:
return HttpResponse(status=404)
if request.method == 'GET':
#Get String Results of 4 queries
jobmststring = JobmstSerializer(mst)
jobdtlstring = JobdtlSerializer(dtl)
jobdepstring = JobdepSerializer(dep)
trgjobstring = TrgjobSerializer(trg)
#Get serialized Results of 4 queries
jobmst_serialized = {'jobmst': jobmststring.data}
jobdtl_serialized = {'jobdtl': jobdtlstring.data}
jobdep_serialized = {'jobdep': jobdepstring.data}
trgjob_serialized = {'trgjob': trgjobstring.data}
jobgroup = jobmst_serialized, jobdtl_serialized, jobdep_serialized, trgjob_serialized,
jobgroupresponse = TESXMLResponse(jobgroup)
return jobgroupresponse
...
It's not perfect but it puts me on the next step of my problem which is customizing the renderer to get the data in the root fields which I have another SO question for :)