How can I dynamically create an endpoints ResourceContainer - python

So using Google Cloud Endpoints, we can create a custom request message and create a endpoints.ResourceContainer from it, like this:
Test_Resource = endpoints.ResourceContainer(message_types.VoidMessage,
param1=messages.IntegerField(2, required=False),
param2=messages.StringField(3, required=False))
And then later on use that in our endpoints.method
#endpoints.method(Test_Resource, MessageCommon, path='list', http_method='POST', name='list')
def model_list(self, request):
pass
My question is that is there any way that we can dynamically create the ResourceContainer by passing a dictionary contains messages types. Something like a factory to create ResourceContainer:
def get_resource_container(messageCls, dict):
new_rc = endpoints.ResourceContainer(messageCls)
# How do I add in the dict's properties to the ResourceContainer here
# ...
return new_rc
This will help not to create new Message class every time. We can keep reuse the same base class:
Test_RC2 = get_resource_container(message.types.VoidMessage, dict)
#endpoints.method(Test_RC2, MessageCommon, path='list', http_method='POST', name='list')
def model_list(self, request):
pass
I tried to search the detail of ResourceContainer but seems like none is available.
Is this possible at all ?

If you want to transform a dict to a list of named parameters you can do : endpoints.ResourceContainer(messageCls, **dict)

Related

Flask Sqlalchemy add multiple row

I am using flask-restful this is
My class I want to insert
class OrderHistoryResource(Resource):
model = OrderHistoryModel
schema = OrderHistorySchema
order = OrderModel
product = ProductModel
def post(self):
value = req.get_json()
data = cls.schema(many=True).load(value)
data.insert()
In my model
def insert(self):
db.session.add(self)
db.session.commit()
schema
from config.ma import ma
from model.orderhistory import OrderHistoryModel
class OrderHistorySchema(ma.ModelSchema):
class Meta:
model = OrderHistoryModel
include_fk = True
Example Data I want to insert
[
{
"quantity":99,
"flaskSaleStatus":true,
"orderId":"ORDER_64a79028d1704406b6bb83b84ad8c02a_1568776516",
"proId":"PROD_9_1568779885_64a79028d1704406b6bb83b84ad8c02a"
},
{
"quantity":89,
"flaskSaleStatus":true,
"orderId":"ORDER_64a79028d1704406b6bb83b84ad8c02a_1568776516",
"proId":"PROD_9_1568779885_64a79028d1704406b6bb83b84ad8c02a"
}
]
this is what i got after insert method has started
TypeError: insert() takes exactly 2 arguments (0 given)
or there is another way to do this action?
Edited - released marshmallow-sqlalchemy loads directly to instance
You need to loop through the OrderModel instances in your list.
You can then use add_all to add the OrderModel objects to the session, then bulk update - see the docs
Should be something like:
db.session.add_all(data)
db.session.commit()
See this post for brief discussion on why add_all is best when you have complex ORM relationships.
Also - not sure you need to have all your models/schemas as class variables, it's fine to have them imported (or just present in the same file, as long as they're declared before the resource class).
You are calling insert on list cause data is list of model OrderHistoryModel instances.
Also post method doesn't need to be classmethod and you probably had an error there as well.
Since data is list of model instances you can use db.session.add_all method to add them to session in bulk.
def post(self):
value = req.get_json()
data = self.schema(many=True).load(value)
db.session.add_all(data)
db.session.commit()

Get request parameter in django syndication?

Here is a url containing the hash for a super-secret feed:
http://127.0.0.1:8000/something/feed/12e8e59187c328fbe5c48452babf769c/
I am trying to capture and send the variable '12e8e59187c328fbe5c48452babf769c' which is feed_hash (acts as a slug to retrieve the particular entry)
Based on the example in django-syndication, I've created this simple class in feeds.py
class SomeFeed(Feed):
title = 'feed title '+request.feed_hash #just testing
link = "/feed/"
description = "Feed description"
def items(self):
return Item.objects.order_by('-published')[:5]
def item_title(self, item):
return item.title
def item_description(self, item):
return item.content
# item_link is only needed if NewsItem has no get_absolute_url method.
def item_link(self, item):
return 'link'
Hence I am wondering, how would I modify this to get a model according to the hash?
At this time I cannot access the 12e8e59187c328fbe5c48452babf769c in any way. How might I access this and -- in a standard Django way -- create a feed from the retrieved variable (which represents a slug accessing a many-to-many relationship.)
First of all, set your parameter in django URL dispatcher. Something like:
url(r'^feed/(?P<pid>\w+)/$', SomeFeed())
Now retrieve and return the hash from the URL using the get_object method on your feed class. After all, get the hash as the second parameter of your method items().
class SomeFeed(Feed):
def get_object(self, request, pid):
# expect pid as your second parameter on method items()
return pid
# you can also load an instance here and get it the same way on items()
return SomeFeed.objects.get(pk=pid)
def items(self, feed):
# filter your feed here based on the pid or whatever you need..
return Item.objects.filter(feed=feed).order_by('-published')[:5]

Django REST Framework: creating hierarchical objects using URL arguments

I have a django-rest-framework REST API with hierarchical resources. I want to be able to create subobjects by POSTing to /v1/objects/<pk>/subobjects/ and have it automatically set the foreign key on the new subobject to the pk kwarg from the URL without having to put it in the payload. Currently, the serializer is causing a 400 error, because it expects the object foreign key to be in the payload, but it shouldn't be considered optional either. The URL of the subobjects is /v1/subobjects/<pk>/ (since the key of the parent isn't necessary to identify it), so it is still required if I want to PUT an existing resource.
Should I just make it so that you POST to /v1/subobjects/ with the parent in the payload to add subobjects, or is there a clean way to pass the pk kwarg from the URL to the serializer? I'm using HyperlinkedModelSerializer and ModelViewSet as my respective base classes. Is there some recommended way of doing this? So far the only idea I had was to completely re-implement the ViewSets and make a custom Serializer class whose get_default_fields() comes from a dictionary that is passed in from the ViewSet, populated by its kwargs. This seems quite involved for something that I would have thought is completely run-of-the-mill, so I can't help but think I'm missing something. Every REST API I've ever seen that has writable endpoints has this kind of URL-based argument inference, so the fact that django-rest-framework doesn't seem to be able to do it at all seems strange.
Make the parent object serializer field read_only. It's not optional but it's not coming from the request data either. Instead you pull the pk/slug from the URL in pre_save()...
# Assuming list and detail URLs like:
# /v1/objects/<parent_pk>/subobjects/
# /v1/objects/<parent_pk>/subobjects/<pk>/
def pre_save(self, obj):
parent = models.MainObject.objects.get(pk=self.kwargs['parent_pk'])
obj.parent = parent
Here's what I've done to solve it, although it would be nice if there was a more general way to do it, since it's such a common URL pattern. First I created a mixin for my ViewSets that redefined the create method:
class CreatePartialModelMixin(object):
def initial_instance(self, request):
return None
def create(self, request, *args, **kwargs):
instance = self.initial_instance(request)
serializer = self.get_serializer(
instance=instance, data=request.DATA, files=request.FILES,
partial=True)
if serializer.is_valid():
self.pre_save(serializer.object)
self.object = serializer.save(force_insert=True)
self.post_save(self.object, created=True)
headers = self.get_success_headers(serializer.data)
return Response(
serializer.data, status=status.HTTP_201_CREATED,
headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Mostly it is copied and pasted from CreateModelMixin, but it defines an initial_instance method that we can override in subclasses to provide a starting point for the serializer, which is set up to do a partial deserialization. Then I can do, for example,
class SubObjectViewSet(CreatePartialModelMixin, viewsets.ModelViewSet):
# ....
def initial_instance(self, request):
instance = models.SubObject(owner=request.user)
if 'pk' in self.kwargs:
parent = models.MainObject.objects.get(pk=self.kwargs['pk'])
instance.parent = parent
return instance
(I realize I don't actually need to do a .get on the pk to associate it on the model, but in my case I'm exposing the slug rather than the primary key in the public API)
If you're using ModelSerializer (which is implemented by HyperlinkedModelSerializer) it's as easy as implementing the restore_object() method:
class MySerializer(serializers.ModelSerializer):
def restore_object(self, attrs, instance=None):
if instance is None:
# If `instance` is `None`, it means we're creating
# a new object, so we set the `parent_id` field.
attrs['parent_id'] = self.context['view'].kwargs['parent_pk']
return super(MySerializer, self).restore_object(attrs, instance)
# ...
restore_object() is used to deserialize a dictionary of attributes into an object instance. ModelSerializer implements this method and creates/updates the instance for the model you specified in the Meta class. If the given instance is None it means the object still has to be created, so you just add the parent_id attribute on the attrs argument and call super().
So this way you don't have to specify a read-only field, or have a custom view/serializer.
More information:
http://www.django-rest-framework.org/api-guide/serializers#declaring-serializers
Maybe a bit late, but i guess this drf nested routers library could be helpful for that operation.

How to add parameters to urls in Django?

I have a view that filters the field called "defaultfieldname" in a certain object_list. What I want to do is to adapt it to pass the name of the field as as parameter in urls.py, so I could use different urls for different fields.
I am not sure which way would be easier:
url(r'^calendar/birthday/$', login_required(MonthCalends.as_view(model=Person)), name='bday_list', filter_field="birthdate"),
url(r'^calendar/deathday/$', login_required(MonthCalends.as_view(model=Person)), name='dday_list', filter_field="deathdate"),
or
url(r'^calendar/birthday/$', login_required(MonthCalends.as_view(model=Person, filter_field="birthdate")), name='bday_list'),
url(r'^calendar/deathday/$', login_required(MonthCalends.as_view(model=Person, filter_field="deathdate")), name='dday_list'),
Then I have a view:
class MonthCalends(ListView):
template_name='month_list.html'
## Sets default fieldname value
filter_field = "defaultfieldname"
...rest of code
The param in urls.py should overwrite the "defaultfieldname" on the view, but I don't know how to get the filter_field from the urls.py in the view. Any help?
Thanks!
The arguments you send with as_view are set on the MonthCalends object. That means filter_field is available as self.filter_field. Assuming you have defined the get method you could do as follows:
class MonthCalends(ListView):
template_name='month_list.html'
## Sets default fieldname value
filter_field = "defaultfieldname"
def get(self, request, *args, **kwargs):
try:
# if the filter field was sent as an argument
filter_field = self.filter_field
except:
# else revert to default
filter_field = MonthCalends.filter_field
# ...rest of code
For a more full explanation check the Django class based views documentation.
You may just use one url, that triggers the second part of your url:
url(r'^calendar/(\w+)$', login_required(MonthCalends.as_view(model=Person)), name='bday_list'),
Then you may access it using self.args[0]
And in case you just permit two different types for filter_field, you may just raise an exception later in the class that you have read self.args[0].
Of course, you may use more readable syntax in the regex like:
r'^calendar/(?P<type>\w+)$'
In this case you can access it using self.kwargs['type'].
Anyway, using regex groups seems much neater.

In Django, is request.META[] the right place to add specific information for a request?

I'm using Django, and want to store data that is relevant only for the duration of a request, and not on the session.
Is it correct to add something to request.META, like:
request.META['acl'] = acl
In my situation, I am using Tastypie, with a custom authorization class, and need a way to pass data between functions... it seems like storing something on the request would be the right thing to do... I just don't know where to store such information. My class looks something like:
class MyAuthorization(Authorization):
def is_authorized(self, request, object=None):
acl = getMyAccessControlList(request.method,request.session['username'])
for permission in acl:
if permission in self.permissions[request.method]:
request.META['acl'] = acl
return True
return False
def apply_limits(self, request, object_class, rs):
if 'HAS_ALL_ACCESS' in request.META['acl']:
return rs
else if 'HAS_USER_ACCESS' in request.META['acl']:
rs = rs.filter(object_class.user==request.session['username'])
return rs
Futher, Tastypie creates a single REST resource object, with a single authorization class used by all threads, so it's not thread-safe to just put it on the authorization class.
UPDATE
As per Chris Pratt's feedback, no, it doesn't make sense to modify the request. Exploring further, it appears to be appropriate to modify the request initially through custom middleware, and then keep it constant for the rest of the request: https://docs.djangoproject.com/en/1.4/topics/http/middleware
In this case, the middleware will look something like:
class AccessControlListMiddleware(object):
def process_view(self,request,view_func,view_args,view_kwargs):
permissions = set()
for role in request.session['permissions']:
for permission in PERMISSION_LIST[request.method][role]:
permissions.add(permission)
request.acl = list(permissions)
No. Don't mess with the request object. Especially since these are methods on the same class, you should simply assign data to self:
self.acl = getMyAccessControlList(request.method,request.session['username'])
...
if 'HAS_ALL_ACCESS' in self.acl:

Categories

Resources