Creating manytomany objects on a fresh django db object - django

I am using Django-rest-framework and am trying to add tags to my models.
Every thing is ready on the database-side, but how do I do it on the django-rest side?
Simplified, my model looks like:
name = models.CharField()
tags = models.ManyToManyField(Tags)
I am presenting the tags as a comma-list in django-rest to make it easy for people using the API to add and change tags. However, how can I add tags to a object that doesn’t exists yet?
Using django-rest restore_object in my serializer, I am able to create the list of manytomany objects, but how do I add them to django-rest attrs so it will add them to my object?
In short, how can I add a list of items to .tags in django-rest restore_object function?
Or is this impossible and I need to do the tags handeling -after- the object is created and therefor hide the "tags" field when creating the object in django-rest and display it at the detailed serializer page instead?

I'm not sure if this is the best method, but this seems to work with overriding the save_object method in the serializer.
def save_object(self, obj, **kwargs):
super(MySerializer, self).save_object(obj, **kwargs)
tags = self.init_data.get('tags', None)
if tags:
obj.tags.clear()
tags = tags.split(',')
for t in tags:
tag_obj, created = Tag.objects.get_or_create(name=t, owner=self.context['request'].user)
obj.tags.add(tag_obj)
Is it a bad idea to grab this data from init_data? Seems a little dirty...

Related

How to do an Ajax form field on Flask Admin

I'm building a Flask-Admin app that uses a REST API as its backend database, which I've implemented as my own BaseModelView. I've got it working with a custom model, and implemented all the functions to edit and save the model.
However I have one field, User, which needs to search another REST API endpoint for a User ID based on name/email, and as the list of users is expected to become quite large I want that field to be an AJAX lookup as I've seen in the SQLAlchemy example looking up a foreign key via Select2/Ajax.
As I need my own Ajax lookup, from following the docs and reading through the code, I'm struggling to find a working example of how to implement it myself and was wondering if anyone can direct me.
I've tried implementing an AjaxSelectField and using the form_ajax_refs property of BaseModelView to override the User field of the form (created in the scaffold_form method) without success.
Does anyone have an example of how I can put a custom Select2 Ajax lookup field, that looks up data from a REST API, on my model form so I can search for a User ID to assign to the model when I create/edit a model?
Figured it out finally, you have to add 2 things to your BaseModelView to do this:
In your BaseModelView class - Add an AjaxSelectField to your form with a custom model form:
def scaffold_form(self):
class AccountForm(Form):
owner = AjaxSelectField(UserAjaxModelLoader('owner'),
label='Owner', blank_text="Select User...")
return AccountForm
Then add a reference to your form_ajax_refs field:
form_ajax_refs = {
'owner': UserAjaxModelLoader('owner')
}
Finally the implementation of the UserAjaxModelLoader class which provides the AJAX endpoint and strings it all together:
from flask_admin.model.ajax import AjaxModelLoader, DEFAULT_PAGE_SIZE
class UserAjaxModelLoader(AjaxModelLoader):
def __init__(self, name, **options):
super(UserAjaxModelLoader, self).__init__(name, options)
def format(self, model):
if model:
return (model.uid, model.email)
return None
def get_one(self, pk):
return auth.get_user(pk)
def get_list(self, query, offset=0, limit=DEFAULT_PAGE_SIZE):
# Put your code to search REST API for users here
return users

How to update primary key field using Django Rest Framework

I have set field called cus_id into primary key field which is not AutoField.
I need to change pk alon to keep other relevant data for same id should be in same index instead pk alone need to be change.
if i gave request via PATCH/PUT it creates new record instead of updating PK
so i have gone through django docs which says,
The primary key field is read-only. If you change the value of the primary key on an existing object and then save it, a new object will be created alongside the old one.
but i am using Django Rest Framework for my API generation..
I can achieve it through overriding DRF GET methods. but i want to do without overridden.
is there any django way to update PK using DRF without overriding get method?
My view:
class ModelViewSet(ModelCustomViewSet):
model = Model
queryset = Model.objects.all()
serializer_class = ModelSerializer
filter_fields = model._meta.get_all_field_names()
filter_backends = [DjangoFilterBackendExt, ]
Serializer:
class ModelSerializer(RequiredMixin):
class Meta:
model = Model
update_lookup_field = "cus_id"
I'd suggest you forget about using custom field for PK and go with regular auto id. Alongside you still can use you your cus_id, requiring it to be non-nullable and unique. That way you can update it via a regular form with absolutely no problems.
Otherwise, you can do update it as you do, clone old instance fields contents into new one and delete old one.

Django rest framework hyperlinkrelatedfield for one table using primary key

I have a table called 'users' and 'location'. Users table has a foreign key that relates to location table. I have a users serializer to get the JSON. What would I do to get the hyperlinks for the users table using its primary key?
In django rest framework documentation, I couldn't find a solution. I tried using hyperlinkrelatedfield. But still I couldn't achieve this. Can someone help me in finding the solution?
Using rest-framework HyperlinkedRelatedField does not work because it was never built to expose the URL of the object being requested. Mainly because since the client already has the url of the user, why send it back again? Nevertheless you can achieve this by doing something like this.
class UserSerializer(serializers.ModelSerializer):
user_url = serializers.SerializerMethodField()
class Meta:
model = User
def get_label_location(self, obj):
return HyperlinkedRelatedField(view_name='user-detail',
read_only=True) \
.get_url(obj, view_name='label-detail',
request=self.context['request'], format=None)
Take note on a few things,
view-name param to the HyperlinkedRelatedField should be based on your url configuration
read-only has to be true since otherwise you'll have to specify the queryset. But since we have the object needed to generate the url we can ignore that.
I've set format param to None but you might want to set it based on your settings.
You can read up about SerializerMethodField here.

How to modify Django Rest Framework incoming request?

I am building a web app using Django that is pretty much only serving as the API server. I have a single-page application that connects to it as well as an Android client. I have a need to modify some of the incoming POST requests that are coming through.
My two use cases:
If during the registration process the user does not select an avatar image to upload (which is a simple TextField that is the URL to the image), I should be able to insert the default avatar URL. So something like if request.data["avatar"] is None: <use default>
The incoming "timestamp" requests from the Android client are all unix timestamps. I would like to convert this to Django's datetime on the fly - so, current request comes in with date_time = 1473387225, I'd like to convert that to a DateTime object.
Now, I'm already doing something similar for certain POST parameters. The way I do it right now is in the post() function of my generic ListCreateApiView I would directly modify the request object and then call the self.create() with that new request object. Is this the right way, or is there a much better way to do it?
Thanks!
If you are using django-rest-framework these things can be done by serializers.
For avatar use an URLField with default value.
For the timestamp you should probably create a custom field.
Check out this site: http://www.cdrf.co It is an easily navigable display of all the methods available on a given class. You can simply use this to overwrite the View you are using. If a model ViewSet, you likely want perform_create and perform_update.
I often do something like this:
class SomeViewSet(viewsets.ModelViewSet):
queryset = SomeModel.objects.all()
serializer_class = SomeModelSerializer
def perform_create(self, serializer):
data = self.request.data
# make some changes to self.request here
serializer.save(
#change some things here
field='some new value'
)
You can do this in a number of ways. As a part of your validation or in the to_internal_value of the request serializer or in a custom field serializer.
Heres an example of doing this as a part of a custom field serializer.
class AccountCreationSerializer(serializers.Serializer):
avatar = AvatarField(
required=False
allow_files=True
)
# Custom Field Serializer
class AvatarField(serializers.FilePathField):
def to_internal_value(self, value):
user_defined_path = super(AvatarField, self).to_internal_value(value)
if user_defined_path:
return user_defined_path
return default_path

Django Rest Framework Dictionary Field

I'm using Mongodb with mongoengine as a backend for a API in Django.
The framework I'm using to create the api is Django Rest Framework.
I need to store a dictionary in a field in Mongo and the best I've done when the method post is called is to use a charfield and parse the dictionary in the function restore_object.
There is a better way to achieve this goal?
It's better to create a dict field? I don't know how hard this could be.
Thank you.
edited to show some code, notice that I store the dictionary as a dict (DictField) and it's content could change from one object to other.
my mongoengine model is something like:
class MyDoc(mongoengine.Document):
name = mongoengine.StringField(max_length=200)
context = mongoengine.DictField()
and my serializer something like:
class MyDocSerializer(serializers.Serializer):
name = serializers.CharField(max_length=200)
context = serializers.CharField()
url = serializers.HyperlinkedIdentityField(
view_name="drf:mydoc-detail",)
def __init__(self,*args,**kwargs):
super(MyDocSerializer,self).__init__(*args,**kwargs)
def restore_object(self, attrs, instance=None):
# Parse string to dict
# this is so ugly, notice I had to repace ' for " to
# avoid an error parsing the json
context = JSONParser().parse(
StringIO.StringIO(
attrs['context'].replace("'","\"")
)
)
attrs['context'] = context
if instance is not None:
instance.name = attrs['name']
instance.context = context
return instance
return MyDoc(**attrs)
Rather than deal with the dictionary field in the Serializer's restore_object, you'll probably end up with something slightly cleaner, if instead you use a custom field for the dictionary field, that manages converting between the dictionary representation and internal char based storage.
You'll want to subclass serializers.WritableField and override the to_native() and from_native methods.
Relevant docs here.
Note: WritableField class that was present in version 2.x no longer exists. You should subclass Field and override to_internal_value() if the field supports data input.
Update: As of 3.0.4 you can now use serializers.DictField... http://www.django-rest-framework.org/api-guide/fields/#dictfield