1

I have used django rest api to create an api that looks like this -

{
"count": 2,
"next": null,
"previous": null,
"results": [
    {
        "url": "http://127.0.0.1:8000/app/student/1/",
        "id": 1,
        "title": "mr",
        "name": "student1",
        "address": "somewhere",
        "city": "Mumbai",
        "tests": [
            {
                "test_name": "Math",
                "section_1": "34",
                "section_2": "54",
                "date_added": "2015-12-15"
            }
        ]
    },
    {
        "url": "http://127.0.0.1:8000/app/student/2/",
        "id": 2,
        "title": "mr",
        "name": "student2",
        "address": "somewhere",
        "city": "Delhi",
        "tests": [
            {
                "test_name": "English",
                "section_1": "34",
                "section_2": "65",
                "date_added": "2015-12-15"
            }
        ]
    }
   ]
 }

I have another app in the same project the uses this data using something like this -

def Peoplelist(request):
    data= requests.get('http://127.0.0.1:8000/app/students/').json()
    send_list = []
    for i in range(2):
        send_list.append(data['results'][i]['name'])
    context = RequestContext(request, {
    'send_list': send_list,
})
return render_to_response('taskmanager/numbers.html', context)

which creates a list of the student names.

I want to display a list of student names depending on the city name and then click on the names to view the student and test details. Can't figure out how to do that. Can someone suggest a way?Thanks.

models.py
class Test(models.Model):
    date_added = models.DateField(default=datetime.datetime.now)
    test_name = models.CharField(max_length=200,default='',blank=False)
    section_1 = models.CharField(max_length=100,default='') 
    section_2 = models.CharField(max_length=100,default='') 

    def __str__(self):
        return self.test_name

class Person(models.Model):
    tests = models.ManyToManyField(Test)
    title = models.CharField(max_length=3,default="mr",blank=False)
    name = models.CharField(max_length=50,default='',blank=False)
    address = models.CharField(max_length=200,default='',blank=False)
    city = models.CharField(max_length=100,default='',blank=False)

    def __str__(self):
        return self.name

serializers.py
class TestSerializer(serializers.ModelSerializer):
    class Meta:
        model = Test
        fields = ('test_name','section_1','section_2','date_added')

class PersonSerializer(serializers.HyperlinkedModelSerializer):
    tests = TestSerializer(many=True, read_only=True)
    class Meta:
        model = Person
        fields = ('id','title', 'name', 'address', 'city','tests')

numbers.html

{% extends "index.html" %}
{% block names %}
{% for list in send_list %}
    <a href="/"><p>{{list}}</p></a>
{% endfor %}
{% endblock %}

******************************EDIT*************************

models.py

class City(models.Model):
   city_name=models.CharField(max_length=100,default='',blank=False)

   def __str__(self):
      return self.city_name
class Person(models.Model):
   tests = models.ManyToManyField(Test)
   title = models.CharField(max_length=3,default="mr",blank=False)
   name = models.CharField(max_length=50,default='',blank=False)
   address = models.CharField(max_length=200,default='',blank=False)
   city = models.CharField(max_length=100,default='',blank=False)

views.py

class StudentList(generics.ListCreateAPIView):
    queryset = Person.objects.all()
    serializer_class = PersonSerializer


class CityList(generics.ListCreateAPIView):
    queryset = City.objects.all()
    serializer_class = CitySerializer

class CityDetail(generics.ListCreateAPIView):
    city = City.objects.all()
    serializer_class = CitySerializer

class StudentDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
6
  • Can you share your models.py and serializers.py? Commented Dec 15, 2015 at 12:57
  • @DhiaTN added now. Thanks. Commented Dec 15, 2015 at 13:08
  • Please show your template. What have you got so far? Commented Dec 15, 2015 at 13:34
  • @RohitJain Using the view I have a shown above, I only have the list of names. Commented Dec 15, 2015 at 13:40
  • 1
    @SaketMishra Yeah, but how are you using the context data in numbers.html template? You said, you want to show student details on click. How you want that details? In separate page, pop up, or same page? Do you need ajax here? We need to see template file here for any help. Commented Dec 15, 2015 at 13:42

2 Answers 2

3

So basically you need 3 different APIs:

  • /cities/ - Gives you list of cities.
  • /cities/<pk>/students - Gives you list of students in given city.
  • /students/<pk>/ - Gives details of Student with id pk.

So, that single API is not going to handle all 3 pages you want.

You'll need 3 template views:

def cities(request):
    data = requests.get('http://127.0.0.1:8000/app/cities/').json()
    context = RequestContext(request, {
        'cities': data.cities,
    })
    return render_to_response('taskmanager/cities.html', context)

def Peoplelist(request, pk):
    data = requests.get('http://127.0.0.1:8000/app/cities/' + pk + '/students/').json()
    context = RequestContext(request, {
        'students': data.students,
    })
    # Better rename this one to students.html
    return render_to_response('taskmanager/numbers.html', context)

def student_details(request, pk):
    data = requests.get('http://127.0.0.1:8000/app/students/' + pk).json()
    context = RequestContext(request, {
        'student': data.student,
    })
    return render_to_response('taskmanager/student_detail.html', context)

Configure the urls for these views. Now the templates would be like:

cities.html:

{% extends "index.html" %}
{% block names %}
{% for city in cities %}
    <a href="{% url 'student_list' city.id %}"><p>{{city.name}}</p></a>
{% endfor %}
{% endblock %}

students.html:

{% extends "index.html" %}
{% block names %}
{% for student in students %}
    <a href="{% url 'student_detail' student.id %}"><p>{{student.name}}</p></a>
{% endfor %}
{% endblock %}

student_detail.html:

{% extends "index.html" %}
{% block names %}
{{ student.name }}
<table>
    <tr>
    <th>Test Name</th>
    <th>Section 1</th>
    <th>Section 2</th>
    <th>Date Added</th>
    </tr>
{% for test in student.tests %}
    <tr>
        <td>{{ test.test_name }}</td>
        <td>{{ test.section_1 }}</td>
        <td>{{ test.section_2 }}</td>
        <td>{{ test.date_added }}</td>
    </tr>
{% endfor %}
</table>
{% endblock %}

Modification in models:

Since you've now a City model, you can change the city field to ForeignKey in Person model. Also, let's rename Person to Student:

class Student(models.Model):
   # Removing the tests field. Rather add `student` field in `Test` model
   # tests = models.ManyToManyField(Test)
   title = models.CharField(max_length=3,default="mr",blank=False)
   name = models.CharField(max_length=50,default='',blank=False)
   address = models.CharField(max_length=200,default='',blank=False)
   city = models.ForeignKey(City)

then change the Test model:

class Test(models.Model):
    student = models.ForeignKey(Student)
    date_added = models.DateField(default=datetime.datetime.now)
    test_name = models.CharField(max_length=200,default='',blank=False)
    section_1 = models.CharField(max_length=100,default='') 
    section_2 = models.CharField(max_length=100,default='') 

    def __str__(self):
        return self.test_name

For views:

  • /cities/ - For this CityList view in your code is fine.
  • /cities/<pk>/students/ - For this, change your StudentList view like this:

    class StudentList(generics.ListCreateAPIView):
        serializer_class = PersonSerializer
    
        def get_queryset(self):
            city = City.objects.get(pk=self.kwargs.get('pk', None))
            students = Student.objects.filter(city=city)
            return students
    

And then for last one:

  • /students/<pk>/ - StudentDetail view would be like:

    class StudentDetail(generics.RetrieveAPIView):
        serializer_class = PersonSerializer
    
        def get_object(self):
            student_id = self.kwargs.get('pk', None)
            return Student.objects.get(pk=student_id)
    

Now for serializers, change your PersonSerializer to this one:

class StudentSerializer(serializers.ModelSerializer):
    test_set = TestSerializer(many=True, required=False)

    class Meta:
        model = Student
        fields = ('id','title', 'name', 'address', 'city', 'test_set')
Sign up to request clarification or add additional context in comments.

7 Comments

Can you give me an idea of how the models.py will be for the relationship between cities and students and how it will be serialized for the three api's you've mentioned.Stuck on it for hours.Thanks
@SaketMishra Student model will have a ForeignKey field to City model. So, for a given City object, you can get students as - city.student_set.all() - that gives you result for second API I wrote there. And then for 3rd API - you just return the Student object for the given primary key.
Also, Student to Test should be a OneToManyField instead of ManyToManyField. One Test score cannot be assigned to 2 different Students. Since OneToManyField doesn't exist, that means have a ForeignKey to Student in Test model.
I've got the first and the third part but for the second api in views.py I'm using class CityDetail(generics.ListCreateAPIView):.How do I add only the list of students in that city to the queryset? Thanks in advance.
@SaketMishra: I see that you don't have a City model, rather have city_name in Student model. Have you added City reference in Student model? as ForeignKey? If that is the case, then for given pk, in second API, you get the city as - City.objects.get(pk=pk), and then city.student_set.all() will get you students.
|
1

If I correctly understand, you want to have hyperlink for students in your api response. Inorder to this add the following in serializer.py:

class StudentByCityField(serializers.RelatedField):
    def to_representation(self, city):
        student_list = self.objects.filter(city=city)
        link_list = list()
        append_link = link_list.append
        for student in student_list:
            append_link("/app/students/%s" % student.name)
        return link_list

class PersonByCitySerializer(serializers.ModelSerializer):
    city_name = StudentByCityField(source='city', read_only=True)
    class Meta:
        model = Test
        fields = ('city_name',)

But in case you want to do this HTML side this will be trivial

  1. views.py
def get_cities(request):
    pesrons = Person.objects.all()
    pesrons.query.group_by = ['city']
    city_set = set([i.city for i in persons])
    context = RequestContext(request, {
        'city_list': list(city_set),
    })
    return render_to_response('cities.html', context)

def get_students_by_city(request, city):
    student_list = Person.objects.filter(city=city)
    context = RequestContext(request, {
        'students': student_list,
    })
    # Better rename this one to students.html
    return render_to_response('students_by_city.html', context)

def get_student_details(request, id):
    student = Person.objects.get(id=id)
    context = RequestContext(request, {
        'student': student,
    })
    return render_to_response('student_detail.html', context)
  1. templates

cities.html

    {% extends "index.html" %}
    {% block names %}
    <ul>
    {% for city in city_list %}
        <li><a href="{% url 'student_by_city' city.name %}">
        {{city.name}}
        </a></li>
    {% endfor %}
    </ul>
    {% endblock %}

students_by_city.html

{% extends "index.html" %}
{% block names %}
<ul>
{% for student in student_list %}
    <li><a href="{% url 'student_detail' student.id %}"><p>{{student.name}}</p></a></li>
{% endfor %}
</ul>
{% endblock %}

student_detail.html

{% extends "index.html" %}
{% block names %}

   <!-- Display here your data as you like -->

{% endblock %}
  1. urls.py
urlpatterns = patterns('',

    url(r'^app/cities/$', views.get_cities, name="participant-register"), 
    url(r'^app/students/(?P<id>\d+)$', views.get_student_by_city, name="student_by_city"),
    url(r'^app/student/(?P<city>\w+/$', views.get_student_detail, name="student_detail"),                       
)

4 Comments

the following statement returns an error - >>> city_list = [i.city for i in city_qs] Traceback (most recent call last): File "<console>", line 1, in <module> TypeError: 'Query' object is not iterable
Ah right I updated the code, It was a typo (wrong variable name) :p
Also, in every view, the Person.objects function is just returning the name of the string like so [<Student: student1>] and not a dictionary and when I try to access student_list.name or student_list.id it just throws the error AttributeError: 'QuerySet' object has no attribute 'name' and likewise. And since no id, even the regex is throwing an error for no reverse match?
you should follow the example, of course student_list is list of object [<Student: student1>], you have to apply llike in the exemple for student in student_list then you can access the student.name, student.id etc. Solution is correct, it's working and I tested it, you need just to follow the example carefully.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.