I have a model with following attributes.
class File(DynamicDocument):
country = fields.StringField(max_length=100, unique=True)
languages = fields.MapField(fields.MapField(
fields.EmbeddedDocumentField(AudioImage)))
I am trying to use Django Rest Framework Mongoengine as follows:
from rest_framework_mongoengine.serializers import DocumentSerializer
class TestSerializer(DocumentSerializer):
class Meta:
model = File
It simply gives the following output:
But I wanted it to address the tree like structure with all the fields from AudioImage class as well.
Did I miss anything? or There is another way for MapField ?
Sijan, is it correct that you want your File documents to have the following structure:
{
"country": "UK",
"languages": {
"hindi": AudioImageJSON,
"russian": AudioImageJSON,
"cockney": AudioImageJSON
}
}
where the structure of AudioImageJSON is described by corresponding EmbeddedDocument?
In that case, your DocumentSerializer is correct and your specify your model as follows:
class AudioImage(EmbeddedDocument):
content = fields.FileField()
class File(DynamicDocument):
country = fields.StringField(max_length=100, unique=True)
languages = fields.MapField(fields.EmbeddedDocumentField(AudioImage))
Note that Browsable API won't be able to display nested form inputs for EmbeddedDocument fields. But you may still use raw data view.
Related
I've been following the documentation for Graphene-Python in Django, and have just made it to the section on custom filters. While a tutorial is provided for how to write custom filters, there isn't a reference on how to call them in GraphiQL. If I have the following example code:
class AnimalNode(DjangoObjectType):
class Meta:
# Assume you have an Animal model defined with the following fields
model = Animal
filter_fields = ['name', 'genus', 'is_domesticated']
interfaces = (relay.Node, )
class AnimalFilter(django_filters.FilterSet):
# Do case-insensitive lookups on 'name'
name = django_filters.CharFilter(lookup_expr=['iexact'])
class Meta:
model = Animal
fields = ['name', 'genus', 'is_domesticated']
#property # make your own filter like this
def qs(self):
return super(EquityFilter, self).qs.filter(id=self.request.user)
class Query(ObjectType):
animal = relay.Node.Field(AnimalNode)
# We specify our custom AnimalFilter using the filterset_class param
all_animals = DjangoFilterConnectionField(AnimalNode,
filterset_class=AnimalFilter)
My question is, what would I need to type in GraphiQL to use this filter? Any help is greatly appreciated.
Inspect the schema in GraphiQL. It should show a root query similar to this one:
allAnimals(
before:String,
after:String,
firts:Int,
last:Int,
name:String,
genus:String,
isDomesticated:Boolean
):AnimalNodeConnection
The three filter criteria are exposed as query parameters, so you can use them with a query like this one:
query filteredAnimals{
allAnimals(
name:"Big Foot",
genus:"Unknown",
isDomesticated:false
) {
edges {
node {
name
genus
isDomesticated
}
}
}
}
Which will give you a connection with undomesticated animals named "Big Foot" ("big FOOT", "Big foot", etc.) with genus equal to "Unknown".
Note: Filters declared on the FilterSet Meta class are named after the type of filtering they do, like name_Icontains, name_Iexact. Filters declared as FilterSet fields (name filter in your case) keep their names unmodified, and extend or OVERRIDE filters declared in the FilterSet Meta class.
I have an existing mongo document which has been exposed over a REST API. The API request will contain certain fields from the document which either needs to be updated with new values or insert new values in them if the field is null. How to perform the update on fields of an existing mongoengine document? I'm using marshmallow-mongoengine for serialization on flask.
The problem that I'm facing is that if a certain field is missing in the request payload, on calling update with the remaining fields as kwargs leads to setting the missing fields as None. How can update or insert only the fields given in the payload?
Joseph's answer is OK. But another answer wont hurt eh!
Here's how i updated my document using flask-mongoengine
Actual code :
Game.objects(id = _id).update(
set__kickoff = request_json.get('kickoff'),
set__gameid = request_json.get('gameid'),
set__home_team = request_json.get('home_team'),
set__away_team = request_json.get('away_team'),
set__home_win = request_json.get('home_win'),
set__draw = request_json.get('draw'),
set__away_win = request_json.get('away_win'),
set__sport = request_json.get('sport')
)
Game class :
import datetime
flask_mongoengine import BaseQuerySet, MongoEngine
db = MongoEngine()
class Game(db.Document):
kickoff = db.DateTimeField(required=True)
added_on = db.DateTimeField(default=datetime.datetime.utcnow)
gameid = db.FloatField(required=True)
home_team = db.StringField(required=True)
home_win = db.FloatField(required=True)
draw = db.FloatField(required=True)
away_win = db.FloatField(required=True)
away_team = db.StringField(required=True)
sport = db.StringField(required=True)
meta = {
'collection':'games',
'queryset_class': BaseQuerySet
}
PS : Remember to indent the code in python
Additionally I noticed you tagged Marshmallow in your question. Here, a sample derived from their official git repo here
First we need a Mongoengine Document:
import mongoengine as me
class Task(me.EmbeddedDocument):
content = me.StringField(required=True)
priority = me.IntField(default=1)
class User(me.Document):
name = me.StringField()
password = me.StringField(required=True)
email = me.StringField()
tasks = me.ListField(me.EmbeddedDocumentField(Task))
Great ! Now it's time for the Marshmallow Schema. To keep things DRY, we use marshmallow-mongoengine to do the mapping:
import marshmallow_mongoengine as ma
class UserSchema(ma.ModelSchema):
class Meta:
model = User
Finally it's time to use our schema to load/dump documents:First let's create a document
user_schema = UserSchema()
u, errors = user_schema.load({"name": "John Doe", "email":
"jdoe#example.com", "password": "123456","tasks": [{"content": "Find a
proper password"}]})
u.save()
If the document already exists, we can update it using update
u
u2, errors = user_schema.update(u, {"name": "Jacques Faite"})
>>> u2.name
"Jacques Faite"
If you only want to update one single document you can use the save method. That's what I do. If a document already exists, it updates fields instead of creating a new document.
car = Car.objects(pk=car_id) # return a queryset
if car:
car = car.get(pk=car_id) # return an object from queryset
car.make = requestData['make']
car.model = requestData['model']
car.mileage = requestData['mileage']
car.save()
If you want to update many documents then, I recommend checking out the atomic updates section of the user guide.
Something like~
Car.objects(param="param to filter by").update(set__param=newParam)
"set" followed by two underscores is a modifier. There are more modifiers available in the guide I linked above.
I am using Relay, Django, Graphene Graphql.
I would like to use django_filters to filter for multiple arguments of type on accommodation. This is described in my schema file and atm looks like:
class AccommodationNode(DjangoObjectType) :
class Meta:
model = Accommodation
interfaces = (relay.Node,)
filter_fields = ['type']
This works perfectly if I pass a single string like: {"accommodationType": "apartment"}, but what if I want to filter for all accommodations that are apartments OR hotels? something like: {"accommodationType": ["apartment","hotel"]}
This is my model:
class Accommodation(models.Model):
ACCOMMODATION_TYPE_CHOICES = (
('apartment', 'Apartment'),
('host_family', 'Host Family'),
('residence', 'Residence'),
)
school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='accommodations')
type = models.CharField(
max_length=200,
choices=ACCOMMODATION_TYPE_CHOICES,
default='apartment'
)
def __str__(self):
return str(self.school) + " - " + self.type
Is there any way I can do this without writing custom filters as are suggested here? For only one filter field this is a great solution but I'll end up having around 50 throughout my application including linked objects...
Have a look at Django REST Framework Filters:
https://github.com/philipn/django-rest-framework-filters
It supports more than exact matches, like in, which you are looking for, but also exact, startswith, and many more, in the same style of Django's ORM. I use it frequently and have been impressed - it even integrates with DRF's web browseable API. Good luck!
like FlipperPA mentioned, I need to use 'in'. According to the django_filter docs:
‘in’ lookups return a filter derived from the CSV-based BaseInFilter.
and an example of BaseInFilter in the docs:
class NumberRangeFilter(BaseInFilter, NumberFilter):
pass
class F(FilterSet):
id__range = NumberRangeFilter(name='id', lookup_expr='range')
class Meta:
model = User
User.objects.create(username='alex')
User.objects.create(username='jacob')
User.objects.create(username='aaron')
User.objects.create(username='carl')
# Range: User with IDs between 1 and 3.
f = F({'id__range': '1,3'})
assert len(f.qs) == 3
The answer to my question:
class AccommodationNode(DjangoObjectType) :
class Meta:
model = Accommodation
interfaces = (relay.Node,)
filter_fields = {
'type': ['in']
}
With the argument {"accommodationType": "apartment,hotel"} will work
Url: /user?u=root works
class UserFilter(django_filters.rest_framework.FilterSet):
u = django_filters.rest_framework.CharFilter(name='username', lookup_expr='contains')
class Meta:
model = User
fields = ['username','u']
but when i changed it to
class UserFilter(django_filters.rest_framework.FilterSet):
u = django_filters.rest_framework.CharFilter(name='username', lookup_expr=['contains'])
class Meta:
model = User
fields = ['username','u']
url: /user?u__contains=root doesn't work.
django 1.11.1
django-filter 1.0.4
djangorestframework 3.6.3
Ykh is close, but incorrect. In your second example, the filter is still exposed as u, so filtering by u__contains is a no-op since it's not a recognized name. u__contains is not somehow translated into a u__contains__contains lookup.
Additionally, passing a list or tuple of lookups to lookup_expr might provide a different behavior than you would expect. It is not related to the automatic filter generation that you see with Meta.fields. Instead, it creates a multi-lookup filter (docs). This filter has two inputs:
a text input for the value to filter by
a select widget to select which lookup to use
It accomplishes this by using a django.forms.MultiWidget, so your query would need to be something like /user?u_0=root&u_1=contains.
In general, MultiWidgets are not that compatible with API usage, given the _0 and _1 suffixes.
If you're trying to expose a filter named u__contains, you should do something like:
class UserFilter(django_filters.rest_framework.FilterSet):
u = django_filters.rest_framework.CharFilter(name='username', lookup_expr='exact')
u__contains = django_filters.rest_framework.CharFilter(name='username', lookup_expr='contains')
class Meta:
model = User
fields = ['u', 'u__contains']
And there are several ways to use lookup_expr and the way you write it is incorrect would be "icontains" for contain and "iexact" for exact. Also another well used expressions would be gte and lte.
class UserFilter(django_filters.rest_framework.FilterSet):
u_contain = django_filters.rest_framework.CharFilter(
field_name='username',
lookup_expr='icontains'
)
l_exact = django_filters.rest_framework.CharFilter(
field_name='lastname',
lookup_expr='iexact'
)
class Meta:
model = User
fields = ['u_contain', 'l_exact']
I am trying to implement filtering in one of my data APIs built on Django(non-rel, mongodb-engine) + Tastypie(non-rel). I want to implement filtering on a sub-document inside my main document. The main documents in mongo looks like
{"user_name": "abc", "email": "abc#domain.com", "safety" :{"intra": True, "inter": False, "external": ['a', 'b']}}
The sub-document contains 2 boolean fields and list field. In mongo I can easily query on sub-documents and their fields however I am not able to implement it at the API level.The django model looks like this
from djangotoolbox.fields import DictField, EmbeddedModelField
class UserSafety(models.Model):
user_name = models.CharField()
email = models.EmailField()
safety = DictField() <------- What to use here!
The corresponding Tasty-pie resource looks like
from tastypie_nonrel.fields import DictField, EmbeddedModelField
class UserSafetyResource(ModelResource):
user_name = field.CharField(attribute='user_name')
email = fields.CharField(attribute='email')
safety = DictField(attribute='safety') <----What to use here!
# This part i want to add
class Meta:
filtering = ['safety.intra': ALL, 'safety.inter': ALL]
Eventually I would like it to work this like in an HTTP request
GET http://<server>/my_endpoint/usersafety?safety.intra=true
Any ideas how this can be achieved? May be using either the embedded type fields?