We use Django and django-graphene to provide GraphQL API. We have UUID primary keys in models. How to properly deal with it?
Based on the fact that ids serialization (query) is handled pretty straightforwardly by DjangoObjectType, I will assume your question relates to mutations.
Also, since good practice is rather oriented towards generating ids on server side, I will also ignore the fact that you mention UUID: I have a feeling this logic should be handled outside of the graphql layer.
The question we therefore seem to be left with is:
Is there a graphene-django Input field to validate primary keys used as mutation arguments ?
Couldn't find any doc on that, so I ventured with the following:
def PrimaryKey(model: django.db.models.Model):
""" This contrived way of creating PrimaryKey classes is due to the fact that
parse_literal is a static method of graphene.Scalar.
"""
def parse_literal(node):
if isinstance(node, ast.IntValue):
pk = node.value
elif isinstance(node, ast.StringValue):
pk = int(node.value)
else:
raise GraphQLError(f'Unsupported type for PrimaryKey: {type(node)}')
return model.objects.get(pk=pk)
return type(
f'{model.__name__}PrimaryKey',
(graphene.Scalar,),
dict(
parse_literal=parse_literal,
serialize=lambda x: None,
parse_value=lambda x: None,
)
)
Suggestion of usage (not all models shown):
class CreateProject(graphene.Mutation):
class Arguments:
name = graphene.String()
owner = PrimaryKey(User) # graphene.List(PrimaryKey(User)) works too
ok = graphene.Boolean()
#staticmethod
def mutate(root, info, name: str, owner: User): # owner has been instantiated
pass # do business
Related
Context: I am writing an API (using Flask and MongoEngine) with multiple account types, including perhaps buildings. I need the database to hold some temporary accounts until a particular building registers.
This is how I've been referencing just one type of user:
current_holder_of_stuff = ReferenceField(ActiveUser)
I know GenericReferenceField is also an option, but what if I only want to allow two types of references? Is there anything like:
current_holder_of_stuff = ReferenceField(ActiveUser, TempUser)
Muchos thankos!
It may work to create a parent class of type User and then have inherited classes of ActiveUser and TempUser to deal with the various user types. As for the requirement for current_holder_of_stuff to be two possible document types, you cannot use a single reference field. As you've dismissed using GenericReferenceField then one way might be to add a property method and a StringField with options such as this:
import mongoegine as mdb
class User(mdb.Document):
name = mdb.StringField()
meta = {'allow_inheritance': True}
class ActiveUser(User):
activation_date = mdb.DateTimeField()
class TempUser(User):
date_limit = mdb.DateTimeField()
class Building(mdb.Document):
address = mdb.StringField()
class Stuff(mdb.Document):
user = mdb.ReferenceField(User)
building = mdb.ReferenceField(Building)
currently_with = mdb.StringField(options=['user','building'],required=True)
#property
def current_holder_of_stuff(self):
if self.currently_with == "user":
return self.user
else:
return self.building
You can also use mongoengine's signals to perform checks pre-save to ensure there is only a user or building defined.
May be I'm doing wrong way, so tell me how to do it better.
I'm implementing one class for all instances of some object, say users. (looks like it is pattern 'Table Module' in M. Fowler's "Patterns of Enterprise Application Architecture"). I attached the simplified example of my implementation.
user argument may be of different kinds:
just int ID
dictionary with information about user. it has key id among other
tuple or list with ID as element with index 0
I want to determine right type of input variable user and get ID from it. At the same time I'd like to check if the input is correct at all.
I'm confused about calling __get_id at the beginning of all methods handling user information.
I sure python can propose better and lighter solution of this problem. Or maybe the whole approach is wrong and I should implement another one.
Thanks in advance.
class BadInputError (Exception):
pass
class Users:
def __init__(self):
pass
def __is_wrong_id(self, id):
# code to check if `id` is wrong
return False
def __get_id(self, user):
if isinstance(user, int):
ID = user
elif isinstance(user, tuple) or isinstance(user, list):
ID = user[0]
elif isinstance(user, dict) and user.has_key('id'):
ID = user['id']
else:
raise BadInputError
if self.__is_wrong_id(ID):
raise BadInputError
return ID
def check_user(self, user):
uID = self.__get_id(user)
# ...
def delete_user(self, user):
uID = self.__get_id(user)
# ...
def assign_new_role_to(self, user):
uID = self.__get_id(user)
# ...
Here __get_id(self, user) make an attempt that would seem as overloading, however you're right - it's generally considered bad practice to check if a variable is an instance of a given class. In your definition you would only ever extract the first element of the tuple or list and hope that it corresponds to the ID.
Instead, you should strive to create wrappers of User that support the __get_id() method.
It's hard to tell with this stripped down code, but the problem that I see here is none of the methods actually use self (other than to call ___get_id -- which doesn't use self).
Normally a class stores data and associated methods -- If you just have methods, then it should be a module.
Here, it makes sense for a "User" to know it's ID, so it might be worth creating a "User" class which uses the __get_id function to store an attribute (self.id). Then the Users class (which represents a collection of Users) could just check user.id. e.g.:
class User(object):
def __init__(self, initialize_data):
self.__set_id(intialize_data)
def __set_id(self, data):
if isinstance(user, int):
self.id = user
elif isinstance(user, (tuple, list)):
self.id = user[0]
elif isinstance(user, dict) and user.has_key('id'):
self.id = user['id']
else:
raise BadInputError
class Users(object):
def __init__(self):
self.users = []
def delete_user(self, user):
self.users = [u for u in self.users if u.id != user.id]
Note that this example implementation is nowhere near optimal -- It was meant to show how I would structure the code. A bunch of improvements could probably be made to make it more efficient (i.e., maybe a User could be hashible based on the id in which case Users.users could be a set).
How do you get the model object of a tastypie modelresource from it's uri?
for example:
if you were given the uri as a string in python, how do you get the model object of that string?
Tastypie's Resource class (which is the guy ModelResource is subclassing ) provides a method get_via_uri(uri, request). Be aware that his calls through to apply_authorization_limits(request, object_list) so if you don't receive the desired result make sure to edit your request in such a way that it passes your authorisation.
A bad alternative would be using a regex to extract the id from your url and then use it to filter through the list of all objects. That was my dirty hack until I got get_via_uri working and I do NOT recommend using this. ;)
id_regex = re.compile("/(\d+)/$")
object_id = id_regex.findall(your_url)[0]
your_object = filter(lambda x: x.id == int(object_id),YourResource().get_object_list(request))[0]
You can use get_via_uri, but as #Zakum mentions, that will apply authorization, which you probably don't want. So digging into the source for that method we see that we can resolve the URI like this:
from django.core.urlresolvers import resolve, get_script_prefix
def get_pk_from_uri(uri):
prefix = get_script_prefix()
chomped_uri = uri
if prefix and chomped_uri.startswith(prefix):
chomped_uri = chomped_uri[len(prefix)-1:]
try:
view, args, kwargs = resolve(chomped_uri)
except Resolver404:
raise NotFound("The URL provided '%s' was not a link to a valid resource." % uri)
return kwargs['pk']
If your Django application is located at the root of the webserver (i.e. get_script_prefix() == '/') then you can simplify this down to:
view, args, kwargs = resolve(uri)
pk = kwargs['pk']
Are you looking for the flowchart? It really depends on when you want the object.
Within the dehydration cycle you simple can access it via bundle, e.g.
class MyResource(Resource):
# fields etc.
def dehydrate(self, bundle):
# Include the request IP in the bundle if the object has an attribute value
if bundle.obj.user:
bundle.data['request_ip'] = bundle.request.META.get('REMOTE_ADDR')
return bundle
If you want to manually retrieve an object by an api url, given a pattern you could simply traverse the slug or primary key (or whatever it is) via the default orm scheme?
What would be the best way to get the latest inserted object using AppEngine ?
I know in Django this can be done using
MyObject.objects.latest()
in AppEngine I'd like to be able to do this
class MyObject(db.Model):
time = db.DateTimeProperty(auto_now_add=True)
# Return latest entry from MyObject.
MyObject.all().latest()
Any idea ?
Your best bet will be to implement a latest() classmethod directly on MyObject and call it like
latest = MyObject.latest()
Anything else would require monkeypatching the built-in Query class.
Update
I thought I'd see how ugly it would be to implement this functionality. Here's a mixin class you can use if you really want to be able to call MyObject.all().latest():
class LatestMixin(object):
"""A mixin for db.Model objects that will add a `latest` method to the
`Query` object returned by cls.all(). Requires that the ORDER_FIELD
contain the name of the field by which to order the query to determine the
latest object."""
# What field do we order by?
ORDER_FIELD = None
#classmethod
def all(cls):
# Get the real query
q = super(LatestMixin, cls).all()
# Define our custom latest method
def latest():
if cls.ORDER_FIELD is None:
raise ValueError('ORDER_FIELD must be defined')
return q.order('-' + cls.ORDER_FIELD).get()
# Attach it to the query
q.latest = latest
return q
# How to use it
class Foo(LatestMixin, db.Model):
ORDER_FIELD = 'timestamp'
timestamp = db.DateTimeProperty(auto_now_add=True)
latest = Foo.all().latest()
MyObject.all() returns an instance of the Query class
Order the results by time:
MyObject.all().order('-time')
So, assuming there is at least one entry, you can get the most recent MyObject directly by:
MyObject.all().order('-time')[0]
or
MyObject.all().order('-time').fetch(limit=1)[0]
So basically, I've got a rather large Django project going. It's a private web portal that allows users to manage various phone-related tasks.
Several pages of the portal provide a listing of Model objects to users, and list all of their attributes in a HTML table (so that users can visually look through a list of these items).
The problem I'm having is: I cannot find a Django-ish or pythonic way to handle the sorting of these Model objects by field name. As an example of what I'm talking about, here is one of my views which lists all Partyline Model objects:
def list_partylines(request):
"""
List all `Partyline`s that we own.
"""
# Figure out which sort term to use.
sort_field = request.REQUEST.get('sortby', 'did').strip()
if sort_field.startswith('-'):
search = sort_field[1:]
sort_toggle = ''
else:
search = sort_field
sort_toggle = '-'
# Check to see if the sort term is valid.
if not (search in Partyline._meta.get_all_field_names()):
sort_field = 'did'
if is_user_type(request.user, ['admin']):
partylines = Partyline.objects.all().order_by(sort_field)
else:
partylines = get_my_partylines(request.user, sort_field)
variables = RequestContext(request, {
'partylines': partylines,
'sort_toggle': sort_toggle
})
return render_to_response('portal/partylines/list.html', variables)
The sorting code basically allows users to specify a /url/?sortby=model_field_name parameter which will then return a sorted listing of objects whenever users click on the HTML table name displayed on the page.
Since I have various views in various apps which all show a listing of Model objects, and require sorting, I'm wondering if there is a generic way to do this sorting so that I don't have to?
I'm sorry if this question is a bit unclear, I'm struggling to find the right way to phrase this question.
Thanks.
The way that I'd look at doing this is through a custom QuerySet. In your model, you can define the class QuerySet and add your sorting there. In order to maintain all the logic in the model object, I'd also move the contents of get_my_partylines into the QuerySet, too.
## This class is used to replicate QuerySet methods into a manager.
## This way: Partyline.objects.for_user(foo) works the same as
## Partyline.objects.filter(date=today).for_user(foo)
class CustomQuerySetManager(models.Manager):
def get_query_set(self):
return self.model.QuerySet(self.model)
def __getattr__(self, attr, *args):
try:
return getattr(self.__class__, attr, *args)
except AttributeError:
return getattr(self.get_query_set(), attr, *args)
class Partyline(models.Model):
## Define fields, blah blah.
objects = CustomQuerySetManager()
class QuerySet(QuerySet):
def sort_for_request(self, request):
sort_field = request.REQUEST.get('sortby', 'did').strip()
reverse_order = False
if sort_field.startswith('-'):
search = sort_field[1:]
else:
search = sort_field
reverse_order = True
# Check to see if the sort term is valid.
if not (search in Partyline._meta.get_all_field_names()):
sort_field = 'did'
partylines = self.all().order_by(sort_field)
if reverse_order:
partylines.reverse()
return partylines
def for_user(self, user):
if is_user_type(request.user, ['admin']):
return self.all()
else:
## Code from get_my_partylines goes here.
return self.all() ## Temporary.
views.py:
def list_partylines(request):
"""
List all `Partyline`s that we own.
"""
partylines = Partylines.objects.for_user(request.user).sort_for_request(request)
There's a great example of how this is done in a generic way in django.contrib.admin.views.main.ChangeList although that does much more than sorting you can browse it's code for some hints and ideas. You may also want to look at django.contrib.admin.options.ModelAdmin the changelist method in particular to get more context.