I have this models (for ilustration only):
class Customer(models.Model):
created = models.DateTimeField(auto_now_add=True)
class Person(Customer):
first_name = models.CharField(max_lenght=40)
last_name = models.CharField(max_length=40)
# More data related to a person
class Company(Customer):
company_name = models.CharField(max_length=100)
# More data related to a company
As you can see, I can have two "types" of customers, but they are both "customers" (I think this is a textbook example of inheritance). Everything works fine at the database level, but now I need to create an endpoint that will "dinamically" show the customer data based on which "type" of customer it is. Something like this:
[
{
"id": 1,
"first_name": "John",
"last_name": "Doe"
},
{
"id": 2,
"company_name": "Big buck"
},
{
"id": 3,
"first_name": "Jane",
"last_name": "Doe"
}
]
When working within my project, I use things like:
customer = Customer.objects.get(pk=100)
if hasattr(customer, 'person'):
pass # Do some stuff
elif hasattr(customer, 'company'):
pass # Do some other stuff
# Do common stuff related to both customer 'types'
So far, I've managed to work around by using different endpoints for "Person" and "Company" customers, but now I need to get both "types" of customers in a single endpoint. And I can't figure out how to write a serializer to get this!
Looking around I found this example for using polymorphic models, however, since the project is quite big now, I'd like to keep things as "vanila" as possible. So my specific questions are:
Is there a way to create a "polymorphic" serializer using the above model definition?
If django-polymorphic (and django-rest-polymorphic) are the way to go, will using them break the functionallity?
Related
I have a simple model like:
class MyModel(models.Model):
data = JSONField()
The JSONField data is in the following structure:
{
"name": "Brian",
"skills": [
{"id": 4, "name": "First aid"},
{"id": 5, "name": "Second aid"}
]
}
I'd like to create a query that gets a list of MyModels filtered by the id of the skill inside the data.
I've tried a few different avenues here, and can do the work in Python but I'm pretty sure there's a way to do this in Django; I think my SQL isn't good enough to figure it out.
Cheers in advance.
try this
>>> MyModel.objects.filter(data__skills__contains=[{'id':4}, {'id':5}])
more about JSON filter https://docs.djangoproject.com/en/3.1/topics/db/queries/#querying-jsonfield
I'm not sure if the following is a bug/not implemented.
Following situation:
I have the following json snippet in my MongoDB:
[{
"firstname": "Test",
"surname": "Test",
"email_address": "example#example.com",
"country": "Austria",
"holiday_home": {
"address": "",
"address_2": "",
"city": "",
"country": "Germany",
"postal_code": "",
"state_province": ""
}
}]
I managed to display the "first level values" (firstname, surname, email, country) in a "standard view" like this without any issues:
class RegistrantView(ModelView):
column_list = ('firstname', 'surname', 'email_address', 'country')
form = RegistrantForm
Unfortunately I do not manage to access the key/values stored nested in "holiday_home".
I've tried numerous ways, like column_list = ([holiday_home]['country']) but unfortunately didn't succeed.
Hence I'd like to ask if this is even possible using flask-admin with pymongo.
Old question, but as for now, there seems to be no way to do what is requested directly from a PyMongo ModelView. This means, there is no special notation allowing you to get the value from a nested document by default.
That being said, I just shared two solutions on this very specific issue here :
https://github.com/flask-admin/flask-admin/issues/1585
First you can duplicate/override : _get_field_value function in your flask_admin_custom.contrib.pymongo.views so that it handles dotted notation better like "holiday_home.country". A proposal is :
def _get_field_value(self, model, name):
"""
Get unformatted field value from the model
"""
try:
return reduce(dict.get, name.split('.'), model)
except AttributeError:
return model.get(name)
If you don't feel like it is a good idea at all to diverge from official Flask admin code, you can still use a column formatter as pointed by Joost.
To do so, just add a custom name in your column list:
column_list = ("firstname","surname", "email_address" "country")
Then catch this exact name in your column formatter :
column_formatter = {
'country' : _country_formatter
}
Not forgetting to define your formatting function somewhere in your code like :
def _country_formatter(view, context, model, name):
return model['holyday_home']['country']
That's it!
Second solution sounds more like a hack, but I think this is the way this currently done by most people.
Goal
If an object has revealed=true it serializes into:
{
"id":1,
"info":"top secret info",
"revealed":true
}
If an object has revealed=false the info field is null:
{
"id":2,
"info":null,
"revealed":false
}
So for a queryset of objects:
[
{
"id":1,
"info":"top secret info 1",
"revealed":true
},
{
"id":2,
"info":null,
"revealed":false
},
{
"id":3,
"info":"top secret info 3",
"revealed":true
}
]
Is it possible to achieve this inside of a Django Rest Framework Model Serializer class?
class InfoSerializer(serializers.ModelSerializer):
class Meta:
model = Info
fields = ('id', 'info', 'revealed')
Background
The DRF docs discuss some advanced serializer usage, and this other post dives into an example. However it doesn't seem to cover this particular issue.
Ideas
A hacky solution would be to iterate over the serialized data afterwards, and remove the info field for every object that has revealed=false. However 1) it involves an extra loop and 2) would need to be implemented everywhere the data is serialized.
I suggest you make the info field appear on all records, but leave it null when revealed is false. If that's acceptable, you should be able to make it happen with a SerializerMethodField.
Alternatively, you could add a revealed_info attribute to the model class, and expose that through the serializer.
#property
def revealed_info(self):
return self.info if self.revealed else None
Context
I have been having conflict. Currenly, I am creating a question answer application. Each question has a tag, and I want to show the most popular tags (e.g. tags that have the most questions associated with it).
Specifics
I am using django-taggit's TaggableManager to do so. Here is the model definition of the Question:
class Question(models.Model):
tags = TaggableManager()
date = models.DateTimeField(default=timezone.now)
text = models.TextField(null=True)
So now I have the several tags attached to questions.
Question
How do I make a tastypie resource to display the tags and sort them by which tag has the most questions associated with it?
My attempts
I have really only had one decently successful attempt at making this work. Here is the tastypie resource:
class TagResource_min(ModelResource):
def dehydrate(self, bundle):
bundle.data['total_questions'] len(Question.objects.filter(tags__slug=bundle.obj.slug))
return bundle
class Meta:
queryset=Tag.objects.all()
Returns a nice JSON with a variable called total_questions with the number of questions associated with it. Although, dehydrate is after the sort_by part of the request cycle specified here, so I cannot sort the resource by total_questions. Here is the JSON:
{
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 13
},
"objects": [
{
"id": 1,
"name": "testing tag",
"resource_uri": "/full/tag/1/",
"slug": "testing-tag",
"total_questions": 2
},
...]
}
Another attempt I had went a different way trying to make the queryset be formed from the Question model:
queryset=Question.objects.filter(~Q(tags__slug=None)).values('tags', 'tags__slug').annotate(total_questions=Count(Tag))
The problem with this was that the filter function turns the result to dict type instead of models.Model type. The key difference was that models.Model has the attribute pk which is a type of identification, and the dict does not have that.
Any contribution is much appriciated! Thank you!
I am trying to figure out whether I can represent model field choices to clients consuming a tastypie API.
I have a django (1.4.1) application for which I am implementing a django-tastypie (0.9.11) API. I have a Model and ModelResource similar to the following:
class SomeModel(models.Model):
QUEUED, IN_PROCESS, COMPLETE = range(3)
STATUS_CHOICES = (
(QUEUED, 'Queued'),
(IN_PROCESS, 'In Process'),
(COMPLETE, 'Complete'),
)
name = models.CharFIeld(max_length=50)
status = models.IntegerField(choices=STATUS_CHOICES, default=QUEUED)
class SomeModelResource(ModelResource):
class Meta:
queryset = SomeModel.objects.all()
resource_name = 'some_model'
When I look at objects in the API, the name and status fields are displayed as follows:
{
...
"objects":[
{
"name": "Some name 1",
"status": 0
},
{
"name": "Some name 2",
"status": 2
}]
}
I know I can alter SomeModelResource with hydrate/dehydrate methods to display the string values for status as follows, which would have more value to clients:
{
...
"objects":[
{
"name": "Some name 1",
"status": "Queued"
},
{
"name": "Some name 2",
"status": "Complete"
}]
}
But how would the client know the available choices for the status field without knowing the inner workings of SomeModel?
Clients creating objects in the system may not provide a status as the default value of QUEUED is desirable. But clients that are editing objects need to know the available options for status to provide a valid option.
I would like for the choices to be listed in the schema description for SomeModelResource, so the client can introspect the available choices when creating/editing objects. But I am just not sure whether this is something available out of the box in tastypie, or if I should fork tastypie to introduce the capability.
Thanks for any feedback!
You can add the choices to the schema by overriding the method in your resource. If you would want to add the choices to any field (maybe to use with many resources), you could create the method as follows:
def build_schema(self):
base_schema = super(SomeModelResource, self).build_schema()
for f in self._meta.object_class._meta.fields:
if f.name in base_schema['fields'] and f.choices:
base_schema['fields'][f.name].update({
'choices': f.choices,
})
return base_schema
I haven't tested the above code but I hope you get the idea. Note that the object_class will be set only if you use the tastypie's ModelResource as it is being get from the provided queryset.
A simpler solution is to hack the choices information into your help_text blurb.
In our example we were able to do:
source = models.CharField(
help_text="the source of the document, one of: %s" % ', '.join(['%s (%s)' % (t[0], t[1]) for t in DOCUMENT_SOURCES]),
choices=DOCUMENT_SOURCES,
)
Easy peasy, automatically stays up to date, and is pretty much side-effect free.