How to add collation in indexes for mongodb? - django

I work with Mongoengine in Django, got task to sort by first_name, last_name fields ignoring case and leading whitespaces
that is why add collation in queryset:
collation = dict(
locale='en',
caseLevel=False,
caseFirst='off',
strength=3,
numericOrdering=True,
alternate='shifted',
maxVariable='space',
backwards=False,
)
return queryset.collation(collation).order_by('-is_stocked', 'brand', 'model')
Unfortunetly, my queryset takes too long time
I want to speed up it, so start to read about mongodb indexes, but don't understand how add it properly, I tried this:
models.py
class Car(UpdatedAtTimestampMixin, Document):
model = fields.StringField(null=True, db_field='model')
brand = fields.StringField(null=True, db_field='brand')
is_stocked = fields.BooleanField(db_field='isStocked')
meta = {
'collection': 'books',
'strict': False,
'indexes': [
['-is_stocked', 'brand', 'model'],
],
}
The main question is How to include collation in indexes? And will it work with null=True Field?

class Car(UpdatedAtTimestampMixin, Document):
model = fields.StringField(null=True, db_field='model')
brand = fields.StringField(null=True, db_field='brand')
is_stocked = fields.BooleanField(db_field='isStocked')
meta = {
'collection': 'books',
'strict': False,
'indexes': [
{'fields': ['-is_stocked', 'brand', 'model'], 'collation': collation},
],
}

Related

Django can i only pass "id" in POST request, despite displaying nested fields?

in my post requests to OrderProduct model, i want to only have to pass order.id and product.id and it works... untill i add a serializer to retrieve product.name. It might be because i didnt understand documentation about nested requests, but im unable to advance further into my project :(
[
{
"id": 2,
"order": 1,
"product": 1,
}
]
^ here's how it looks without nested serializer, and thats the data that i wanna have to input
[
{
"id": 2,
"order": 1,
"product": {
"id": 1,
"name": "gloomhaven",
},
},
^ here's how it looks after i add an additional serializer. I pretty much want these nested fields to be read only, with me still being able to send simple post requests
here are my serializers
class OrderProductSerializer(serializers.ModelSerializer):
product = Product()
class Meta:
model = OrderProduct
fields = [
"id",
"order",
"product"]
class Product(serializers.ModelSerializer):
class Meta:
model = Product
fields = (
"id",
"name")
Is there any way for me to accomplish this? Thank you for trying to help!
Just overwrite to_representation method of the serializer
def to_representation(self, instance):
response = super().to_representation(instance)
response['other_field'] = instance.id# also response['other_field'] = otherSerializer(instance.model)
return response
This can solve your problem
I think you are missing many=True
class OrderProductSerializer(serializers.ModelSerializer):
product = Product(many=True)
class Meta:
model = OrderProduct
fields = [
"id",
"order",
"product"]

How do I create new models not affecting the nested serializer

I already have a general idea of how it should be done. The only issue that I face now is how to actually send the data. I don't want to create new Projects I just want to add them to the notifications. How do I pass the data, the actual JSON?
class NotificationsScheduleSerializer(ModelSerializer):
projects = ProjectSerializer(many=True) # Thats the Many2Many Field
user = HiddenField(default=CurrentUserDefault())
class Meta:
model = NotificationsSchedule
fields = [
"pk",
"projects",
"period",
"week_day",
"created_at",
"time",
"report_type",
"user",
]
def create(self, validated_data):
breakpoint() # I don't ever get "projects" in validated_data just Empty OrderedDict
projects_data = validated_data.pop("projects", [])
notification = NotificationsSchedule.objects.create(**validated_data)
return notification
class ProjectSerializer(ModelSerializer):
class Meta:
model = Project
fields = ["pk", "name"]
I want to be able to pass something like this.
{
"projects": [290, 289],
"period": "daily",
"week_day": 2,
"time": "16:02:00",
"report_type": "word_report"
}
But it expects dict instead.
"non_field_errors": [
"Invalid data. Expected a dictionary, but got int."
]
You have to set read_only,
projects = ProjectSerializer(many=True, read_only=True)
And when creating Notifications ,
notification = NotificationsSchedule.objects.create(**validated_data)
notification.projects.add(*self.initial_data.get("projects"))
notification.save()

serializer.data is missing some of the data

Context: I am having problem accessing fields which are validated by nested serializers.
I have a very sample model as shown below.
For 2 of the fields I have their specific serializers. When I try to access the data it returns all the fields except the one validated by the specific serializers.
Models looks like this
class Sampler(models.Model):
sample_name = models.CharField(unique=True, max_length=100)
sampling_by = JSONField(max_length=100)
extractions = JSONField(max_length=100)
max_samples = models.IntegerField(default=100)
Serializers
class ExtractionsSerializer(serializers.BaseSerializer):
table_name = serializers.CharField()
extraction_type = serializers.ChoiceField(["ABC"])
dependencies = serializers.ListField(child=RecursiveField(), allow_empty=True, required=False)
class SamplingBySerializer(serializers.BaseSerializer):
"""
Validate sampling_by
"""
def to_internal_value(self, samples):
methods = ["ABC"]
sampling_not_supported = [sample for sample in samples
if sample['by'] not in methods]
if sampling_not_supported:
raise ValidationError("{} not in {}".format(sampling_not_supported, methods))
class SamplerSerializer(serializers.ModelSerializer):
extractions = ExtractionsSerializer(read_only=True)
sampling_by = SamplingBySerializer(read_only=True)
class Meta:
model = Sampler
fields = ('sample_name', 'database', 'schema', 'sampling_by', 'extractions', 'max_samples')
Now I do
data = {
"database": "postgresql://..",
"sampling_by":[{
"by":"ABC",
"value": ["l32=turn_the"]
}],
"max_samples":3,
"extractions" : [{
"table_name": "person",
"extraction_type": "ABC"
}]
}
sampler = SamplerSerializer(data=data)
sampler.is_valid() #returns True
sampler.data => does not contain data of the nested fields. Like the `sampling_by` and `extractions`. Contains all other fields
sampler.validated_data => same problem as above
Any help would be appreciated! thanks
You probably missed the fact that your nested serializers are flagged as read_only=True
class SamplerSerializer(serializers.ModelSerializer):
extractions = ExtractionsSerializer(read_only=True)
sampling_by = SamplingBySerializer(read_only=True)
Remove that part, implement the serializer's create / update and you're good to go.
On a side note, it doesn't make sense to access serializer.data when deserializing.
Edit: the authority source is validated_data.

model field named url and django rest framework url

I started to use the great django-rest-framework days ago.
I'm not able to solve this simple issue.
My model contains a models.URLField named url.
My serializers.py file:
class ModelSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.Field(source='owner.username')
class Meta:
model = Model
fields = ('url', 'owner', 'title', 'abstract', 'category', 'position', 'param1')
Checking the API result the field 'url' is populated with model.URLField.
"results": [
{
"url": "http://www.web.com",
"owner": "me",
"title": "title of the stuff"
}
Instead I would like to have
"results": [
{
"url": "http://localhost:8000/en/apiv1/maps/4/",
"url_in_model": "http://www.web.com",
"owner": "me",
"title": "Forest fire"
}
How can I solve?
Thanks
It may be considered poor form (I am by no means a pro programmer or rest_framework expert), but I believe you can add extra context to the serialized output:
http://django-rest-framework.org/api-guide/serializers.html#specifying-fields-explicitly
class AccountSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
groups = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Account
Extra fields can correspond to any property or callable on the model.
So in the above the field 'get_absolute_url' must be in the 'Account' model.
In your case (I think) you could do this:
class ModelSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.Field(source='owner.username')
url_in_model = serializer.Field(source='url')
class Meta:
model = Model
fields = ('url', 'url_in_model', 'owner', 'title', 'abstract', 'category', 'position', 'param1')
Of course you would pick the field type that suits.
I haven't had the chance to test this, so there is the chance that using 'url' as your source causes an issue and you may want to name your model field something else - apologies if that is the case and I have wasted your time.
Hope I have helped.
The accepted answer didn't work for me in DRF 3. I got my model's url data for both url and url_in_model. To get the correct url value from DRF, it looked like:
class AccountSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='account-detail')
url_in_model = serializer.URLField(source='url')
class Meta:
model = Account
account-detail should be replaced with whatever view corresponds to a single Account.
I personally prefer to set the default hyperlinked field to another name altogether.
You can do this via the URL_FIELD_NAME setting.
Source: http://www.django-rest-framework.org/api-guide/serializers/#changing-the-url-field-name
e.g: URL_FIELD_NAME = 'key' (default is 'url')
The easiest way to avoid the conflict is to set URL_FIELD_NAME in settings.py like:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.BasicAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
"URL_FIELD_NAME": "api_url", // <---- add this
}
the above answers are not precise.It is simply to control the name of url(default is 'url') by using the key "url_field_name".
class UserSerializer(serializers.ModelSerializer):
url_field_name='test-url'
class Meta:
model = UserInfo
extra_kwargs = {
'test-url': {
'view_name': '<your view name defined on urls.py>',
'lookup_field': '<lookup_field>',
'lookup_url_kwarg': '<lookup_url_kwarg>'
}
}
fields = ['test-url'] #remember include your defined url name
the source code of ModelSerializer in serializer.py
def get_fields(self):
"""
Return the dict of field names -> field instances that should be
used for `self.fields` when instantiating the serializer.
"""
if self.url_field_name is None:
self.url_field_name = api_settings.URL_FIELD_NAME
assert hasattr(self, 'Meta'), (
'Class {serializer_class} missing "Meta" attribute'.format(
serializer_class=self.__class__.__name__
)
)
assert hasattr(self.Meta, 'model'), (
'Class {serializer_class} missing "Meta.model" attribute'.format(
serializer_class=self.__class__.__name__
)
)
if model_meta.is_abstract_model(self.Meta.model):
raise ValueError(
'Cannot use ModelSerializer with Abstract Models.'
)
declared_fields = copy.deepcopy(self._declared_fields)
model = getattr(self.Meta, 'model')
depth = getattr(self.Meta, 'depth', 0)
Nginx needs to be configured correctly, put your ip in the PROXY_PASS
location / {
proxy_pass http://127.0.0.1:8003;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
}
Change http://127.0.0.1:8003 by http://this-is-my-ip-address.com/[port]

Djangorestframework Modelresource add a field from an foreignkey

I have an api in django-rest framework that now returns this json data:
[
{
"id": 1,
"foreignobject": {
"id": 3
},
"otherfields": "somevalue"
}
]
But I want it to return something like this (flatten the foreigneky to its ID only):
[
{
"id": 1,
"foreignobject_id":3,
"otherfields": "somevalue"
}
]
Doing this in the model Resource, now I Have (simplified):
class ForeignKeyInDataResource(ModelResource):
model = TheOtherModel
fields = ('id',)
class SomeModelResource(ModelResource):
model = SomeModel
fields = ( 'id',('foreignobject','ForeignKeyInDataResource'),'otherfields',)
I tried already something like:
class SomeModelResource(ModelResource):
model = SomeModel
fields = ( 'id','foreignobject__id','otherfields',)
but that did not work
for the complete story, this how the view returns the data, list is a result of a query over the SomeModel:
data = Serializer(depth=2 ).serialize(list)
return Response(status.HTTP_200_OK, data)
I'm not really in a position to support REST framework 0.x anymore, but if you decide to upgrade to 2.0 this is trivial - simply declare the field on the serializer like so: foreignobject = PrimaryKeyRelatedField()
I found another option: (by reading the ModelResource documentation...)
In the Modelresource you can define a function(self,instance), which can return the id.
in the fields you can add this function!
so, this works:
class SomeModelResource(ModelResource):
model = SomeModel
fields = ( 'id','foreignobject_id','otherfields',)
def foreignobject_id(self, instance):
return instance['foreignobject']['id']