Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django ORM access User table through multiple models

views.py

I'm creating a queryset that I want to serialize and return as JSON. The queryset looks like this:

all_objects = Program.objects.all()
test_data = serializers.serialize("json", all_objects, use_natural_keys=True)

This pulls back everything except for the 'User' model (which is linked across two models).

models.py

from django.db import models
from django.contrib.auth.models import User

class Time(models.Model):
    user = models.ForeignKey(User)
    ...

class CostCode(models.Model):
    program_name = models.TextField()        
    ...

class Program(models.Model):
    time = models.ForeignKey(Time)
    program_select = models.ForeignKey(CostCode)
    ...

Question

My returned data has Time, Program, and CostCode information, but I'm unable to query back the 'User' table. How can I get back say the 'username' (from User Table) in the same queryset?

Note: I've changed my queryset to all_objects = Time.objects.all() and this gets User info, but then it doesn't pull in 'CostCode'. My models also have ModelManagers that return the get_by_natural_key so the relevant fields appear in my JSON.

Ultimately, I want data from all four models to appear in my serialized JSON fields, I'm just missing 'username'.

Here's a picture of how the JSON object currently appears in Firebug: Firefox's Firebug showing the JSON object

Thanks for any help!

like image 886
Will Avatar asked Jun 27 '14 17:06

Will


2 Answers

It seems a bit heavyweight at first glance but you could look at using Django REST Framework:
http://www.django-rest-framework.org/api-guide/serializers#modelserializer

You can define and use the serializer classes without having to do anything else with the framework. The serializer returns a python dict which can then be easily dumped to JSON.

To get all fields from each related model as nested dicts you could do:

class ProgramSerializer(serializers.ModelSerializer):
    class Meta:
        model = Program
        depth = 2

all_objects = Program.objects.all()
serializer = ProgramSerializer(all_objects, many=True)
json_str = json.dumps(serializer.data)

To customise which fields are included for each model you will need to define a ModelSerializer class for each of your models, for example to output only the username for the time.user:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('username', )

class TimeSerializer(serializers.ModelSerializer):
    """
    specifying the field here rather than relying on `depth` to automatically
    render nested relations allows us to specify a custom serializer class
    """
    user = UserSerializer()

    class Meta:
        model = Time

class ProgramSerializer(serializers.ModelSerializer):
    time = TimeSerializer()

    class Meta:
        model = Program
        depth = 1  # render nested CostCode with default output

all_objects = Program.objects.all()
serializer = ProgramSerializer(all_objects, many=True)
json_str = json.dumps(serializer.data)
like image 71
Anentropic Avatar answered Sep 19 '22 13:09

Anentropic


What you really want is a "deep" serialization of objects which Django does not natively support. This is a common problem, and it is discussed in detail here: Serializing Foreign Key objects in Django. See that question for some alternatives.

Normally Django expects you to serialize the Time, CostCode, Program, and User objects separately (i.e. a separate JSON array for each) and to refer to them by IDs. The IDs can either be the numeric primary keys (PKs) or a "natural" key defined with natural_key.

You could use natural_key to return any fields you want, including user.username. Alternatively, you could define a custom serializer output whatever you want there. Either of these approaches will probably make it impossible to load the data back into a Django database, which may not be a problem for you.

like image 42
Anton Avatar answered Sep 21 '22 13:09

Anton