Let's say I have a Django model
class Book(models.Model):
title = models.CharField()
author = models.ForeignKey(Author)
is_on_loan = models.BooleanField()
How do I get the JSON representation of the Book? I could easily write something like
import json
def get_json(self):
json_rep = {}
json_rep['title'] = self.title
json_rep['author'] = self.author.full_name
json_rep['is_on_loan'] = self.is_on_loan
return json.dumps(json_rep)
But is there a better way?
This is called serializing. There are django native serializers, you can read about them in the docs (https://docs.djangoproject.com/en/1.9/topics/serialization/), but the basic usage is
from django.core import serializers
data = serializers.serialize("json", YourModel.objects.all())
That said, Django serializers have some limitations that might or might not cause problems for you.
If you are using or considering using Django REST Framework, it has excellent, flexible serializer/deserializer architecture. There are other serializing libraries out there that you might want to google for.
Yes there is a better way, use the built-in Django JSON serializer
from django.core import serializers
json_data = serializers.serialize('json', Book.objects.all())
Related
Is there any way to create Dynamic queries in django graphene without mentioning object type. What I am currently doing is I created an ObjectType class and with a string type field and when I call the query i send the model name as an argument and serialize the queryset and send the json as the string field. But again, in that case I cannot take the advantages of graphQL, thats just me using graphql to get json data. Does anyone know of a proper way to implement this?
You will have to mention ObjectType, that tells graphene what fields to register and gives you the whole functionality. It's very simple though:
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=128, null=True)
And the in schemas.py:
import graphene
from .models import Book as BookModel
from graphene_django import DjangoObjectType
class Book(DjangoObjectType):
class Meta:
model = BookModel
class Query(graphene.ObjectType):
books = graphene.List(Book, resolver=lambda query, info: Books.objects.all())
schema = graphene.Schema(query=Query)
That's it!
I'm pretty new to django's rest framework and I built my first example API using the official tutorial here. But I have no idea how to consume this API's data into another app in the same project such its data can be rendered into HTML.
Suppose I create an API students for students(with their details) in a school. Now how do I use this API in the same project to display the number of students in schools and their details.
Most of the tutorials or explanations online are for third-party API's and I can't figure out how to proceed. Thanks in advance.
models.py
class Test(models.Model):
date = models.DateTimeField(auto_now_add=True)
test_name = models.CharField(max_length=200,default='simple blood test',blank=False)
subject = 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)
def __str__(self):
return self.name
views.py
class PersonList(generics.ListCreateAPIView):
queryset = Person.objects.all()
serializer_class = PersonSerializer
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class PersonDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Person.objects.all()
serializer_class = PersonSerializer
serializers.py
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = Test
fields = ('test_name','subject')
class PersonSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
tests = TestSerializer(many=True, read_only=True)
class Meta:
model = Person
fields = ('url','id','name')
This is my API definiton. I want to create another app to display data such list of all students and details about them etc.
You will have to hit your endpoints in your consuming view, the easiest way to do this is with the requests library. First install the library:
pip install requests
Then use it in your consuming view:
def consumer_view(request):
response = requests.get('http://your-url.com/your-endpoint')
# do what you need to do here
You can use response.json() to get the JSON response form your API as a Python dictionary. If you are just using ./manage.py runserver your URL will be:
http:localhost:8000/your-endpoint
or
http://192.168.0.1:8000/your-endpoint
This way of consuming an API is somewhat redundant if you are working completely within Django. It's often much easier to use the ORM in these cases. However if you are making the API to be available for outside use (either publicly or via API keys) then this approach makes sense.
I wrote an __init__ method for one of my models that adds some auxiliary information to the object by dynamically adding an attribute to the object that does not reflect a column in the database:
class MyModel(models.Model):
title = Models.CharField()
description = Models.TextField()
def __init__(self, *args, **kwargs):
self.aux_info = "I'm not in the database!"
This seemed to be working fine, but I found a case where it does not work. I have some code in a view where I set a status variable and package up a list of MyModels into json like so:
from django.core import serializers
from django.utils import simplejson
...
# have to use serializers for django models
serialized_items = serializers.serialize("json", itemlist)
data["items"] = serialized_items # serialized_items is now a string
data["status"] = status
# package up data dict using simplejson for python objects
resp = simplejson.dumps(data)
return HttpResponse(resp, mimetype="application/javascript")
The problem seems to be that django's serializers only serialize the model fields and not all attributes of the object so aux_info does not come through. I'm also pretty sure that using both serializers and simplejson is not the right way to do this. Thanks for any help!
Try usung the serialiser's optional fields argument.
serialized_items = serializers.serialize("json", itemlist, fields=['.....', 'aux_info'])
May i also suggest that using the __init__ method to add fields is considdered bad form in django and would be much better achieved like so:
class MyModel(models.Model):
title = Models.CharField()
description = Models.TextField()
def aux_info(self):
return "I'm not in the database!"
In my django site I have two apps, blog and links. blog has a model blogpost, and links has a model link. There should be a one to many relationship between these two things. There are many links per blogpost, but each link has one and only one blog post. The simple answer is to put a ForeignKey to blogpost in the link model.
That's all well and good, however there is a problem. I want to make the links app reusable. I don't want it to depend upon the blog app. I want to be able to use it again in other sites and perhaps associate links with other non-blogpost apps and models.
A generic foreign key seems like it might be the answer, but not really. I don't want links to be able to associate with any model in my site. Just the one that I explicitly specify. And I know from prior experience that there can be issues using generic foreign keys in terms of database usage because you can't do a select_related over a generic foreign key the way you can with a regular foreign key.
What is the "correct" way to model this relationship?
If you think the link app will always point to a single app then one approach would be to pass the name of the foreign model as a string containing the application label instead of a class reference (Django docs explanation).
In other words, instead of:
class Link(models.Model):
blog_post = models.ForeignKey(BlogPost)
do:
from django.conf import setings
class Link(models.Model):
link_model = models.ForeignKey(settings.LINK_MODEL)
and in your settings.py:
LINK_MODEL = 'someproject.somemodel'
I think TokenMacGuy is on the right track. I would look at how django-tagging handles a similar generic relationship using the content type, generic object_id, and generic.py. From models.py
class TaggedItem(models.Model):
"""
Holds the relationship between a tag and the item being tagged.
"""
tag = models.ForeignKey(Tag, verbose_name=_('tag'), related_name='items')
content_type = models.ForeignKey(ContentType, verbose_name=_('content type'))
object_id = models.PositiveIntegerField(_('object id'), db_index=True)
object = generic.GenericForeignKey('content_type', 'object_id')
objects = TaggedItemManager()
class Meta:
# Enforce unique tag association per object
unique_together = (('tag', 'content_type', 'object_id'),)
verbose_name = _('tagged item')
verbose_name_plural = _('tagged items')
Anoher way to solve this is how django-mptt does this: define only an abstract model in a reusable app(MPTTModel), and require to inherit it with defining some fields (parent=ForeignKey to self, or whatever your app usecase will require)
Probably you need to use the content types app to link to a model. You might then arrange for your app to check the settings to do some additional checking to limit which content types it will accept or suggest.
I'd go with generic relations. You can do something like select_related, it just require some extra work. But I think it's worth it.
One possible solution for generic select_related-like functionality:
http://bitbucket.org/kmike/django-generic-images/src/tip/generic_utils/managers.py
(look at GenericInjector manager and it's inject_to method)
This question and Van Gale's answer lead me to the question, how it could be possible, to limit contenttypes for GFK without the need of defining it via Q objects in the model, so it could be completly reuseable
the solution is based on
django.db.models.get_model
and the eval built-in, that evaluates a Q-Object from settings.TAGGING_ALLOWED. This is necessary for usage in the admin-interface
My code is quite rough and not fully tested
settings.py
TAGGING_ALLOWED=('myapp.modela', 'myapp.modelb')
models.py:
from django.db import models
from django.db.models import Q
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.db.models import get_model
from django.conf import settings as s
from django.db import IntegrityError
TAGABLE = [get_model(i.split('.')[0],i.split('.')[1])
for i in s.TAGGING_ALLOWED if type(i) is type('')]
print TAGABLE
TAGABLE_Q = eval( '|'.join(
["Q(name='%s', app_label='%s')"%(
i.split('.')[1],i.split('.')[0]) for i in s.TAGGING_ALLOWED
]
))
class TaggedItem(models.Model):
content_type = models.ForeignKey(ContentType,
limit_choices_to = TAGABLE_Q)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
def save(self, force_insert=False, force_update=False):
if self.content_object and not type(
self.content_object) in TAGABLE:
raise IntegrityError(
'ContentType %s not allowed'%(
type(kwargs['instance'].content_object)))
super(TaggedItem,self).save(force_insert, force_update)
from django.db.models.signals import post_init
def post_init_action(sender, **kwargs):
if kwargs['instance'].content_object and not type(
kwargs['instance'].content_object) in TAGABLE:
raise IntegrityError(
'ContentType %s not allowed'%(
type(kwargs['instance'].content_object)))
post_init.connect(post_init_action, sender= TaggedItem)
Of course the limitations of the contenttype-framework affect this solution
# This will fail
>>> TaggedItem.objects.filter(content_object=a)
# This will also fail
>>> TaggedItem.objects.get(content_object=a)
I am using django-model-utils for inheritance Managers. I want to get results of only one subclass at a time.
managers.py
from model_utils.managers import InheritanceManager
class PostManager(InheritanceManager):
pass
models.py
from .managers import PostManager
class Post(models.Model):
title = models.CharField(max_length=20)
text = models.TextField()
objects = PostManager()
class ImagePost(Post, models.Model):
source = models.URLField()
image = models.ImageField(upload_to="images/%Y/%m/%d")
class VideoPost(Post, models.Model):
source = models.URLField()
I want to return results of only image type. by writing a simpler query like this.
Post.objects.filter(type='image').select_subclasses()
What i have tried:
if type == 'image':
Post.objects.filter(imagepost__isnull=False).select_subclasses()
This works but is kind of anti-pattern, i don't want to write conditions in views for every content type.
Is there better way like defining a property in models or converting it into a manager method? or am i missing something?
Have you tried to pass the class to select_subclasses method?
Post.objects.select_subclasses(ImagePost)
Check their doc about this feature.
Edit:
I misunderstood the question, but sounds like OP wants only the Post with type ImagePost. Doing select_subclasses(ImagePost) would fetch everything and convert the objects with type ImagePost to ImagePost instances. The solution should be as simple as :
image_posts = ImagePost.objects.all()