No CRUD operations with Django/Tastypie - django

I am a newbie with Tastypie and it is wonderful the way you can achieve CRUD operations with it so quickly. But I would like to implement other kind of web services where the return value is other than a model. For example, if I had a simple model like this
class User(models.Model):
name = models.CharField(max_length=20)
age = models.PositiveSmallIntegerField()
and wanted to get the average age of all users via /api/v1/user/avg_age, how should I do it? Perhaps it is something related to Django URLs more than Tastypie but I am lost at this moment. So, the question is where/how should I define my custom REST web services?
Thanks in advance

You can add the method to the model itself or put it in a service layer. After doing so you can easily add the value to the resource with a dehydration cycle.
Another option, which will allow filtering on the value, is to implement a model holding this data, e.g. a UserStatistics model. You can then add a foreign key relationship or create a stand-alone resource.
Because data won't likely change a whole lot and these calculations are more expensive I would encourage you to create a cronjob or task for such a model, only executing database writes periodically

Related

Django Graphene/GraphQL best design for model field-level permissions

I am using Django as back-end with graphene-django serving the front-end. I am new to both django and graphene so I am not sure what is the best approach to achieve field-level permissions with no code repetition in this set-up. For example, if my model is:
class MyModel(models.Model):
field1 = models.CharField()
field2 = models.CharField()
I want to be able to specify that user1 can read field1 but not field2; when the user1 queries GraphQL for all MyModels it would be only allow to retrieve field1 from rows (nodes) and not field2.
I did a bit of research and found 2 possible alternative solutions but I fear they may not be in the spirit of Django framework and/or conflicting with each other and there may be a better way to achieve this in Django. Ultimately I don't want to be repeating the permissions code in multiple parts of the Django and Graphene back-end so want this to be centralised at the lowest possible level, ideally at the Django model. I need to be able to control full CRUD per field depending on user_id and maybe even have some additional logic. The options I found were:
overriding get_node resolver in graphene-django DjangoObjectType to check for permissions there. Personally I see this as a very bad and last-resort solution as the checks are done only on graphql query/mutation layer and not elsewhere in Django. I could easily write a Django form or view that would not benefit from the permission check unless this is coded again in that form/view.
I could extend the Django model to perform arbitrary per-field checks and this seems the right level where to enforce permission checks but ideally I would prefer to use built-in features or a 'popular' library for this type of stuff. I tried searching for a library but I couldn't find anything that is even remotely production ready or gaining any traction - which leads me to the consider that there may be a better approach to address this problem. Django-field-permissions package seemed on the right path though.
I was wondering if anyone has a view on the best approach to solving this problem that fits Django and Graphene frameworks and without repeating the permissions code everywhere?
You're not going to find a stable/popular package for this, as it's not a design that a database can support well.
You cannot link (Foreign Key) a field and a table, you can only link two tables using a field. Therefore, any work to determine whether a row in a table has access to a field in another table, will be costly and error prone.
The best you could do, is to write a service layer that sits in between a resolver and a model that nullifies fields a user has no access to. You pass it the user (info.context.user) and a model, and it does a separate query to a field permissions model, fetches the record and nullifies each field according to permissions.
This inherently means each field needs to be nullable in order to support the permissions, complicating the frontend - it's a lot of work...
A better approach if your business logic allows it, is to group those fields into models, that can then be tied to "roles" or groups. So if this were medical data in a hospital:
- name ----- \
- address | -> Person model => all personnel, except custodial
- birth date -/
- medication -------\
- patient history | => PatientStatus model => all medically trained personnel
- current physician /
- ...
That way you use standard built-in permissions to deny access to entire tables (as illustrated above) and object level permissions (Guardian, Authority) to deny access to all but the current physician for the really classified things.
Hope this helps and good luck!

Beginner Questions about django-viewflow

I am working on my first django-viewflow project, and I have some very basic questions. I have looked at the docs and the cookbook examples.
My question is which fields go into the "normal" django models (models.Model) and which fields go into the Process models? For example, I am building a publishing model, so a document that is uploaded starts in a private state, then goes into a pending state after some processing, and then an editor can update the documents state to publish, and the document is available through the front facing web site. I would assume the state field (private, pending, publish) are part of a process model, but what about the other fields related to the document (author, date, source, topic, etc.)? Do they go into the process model or the models.Model model? Does it matter? What are the considerations in building the models and flows for separation of data between the two types of models?
Another example - why in the Hello World example is the text field in the Process model and not a model.Models model? This field does not seem to have anything to do with the process, but I am probably not understanding how viewflow works.
Thanks!
Mark
That's your choice. Viewflow is the library and has no restriction on data alignment. The only thing that needs to be done is the link between process_pk and the process data. HelloWord is the minimal working sample, that demonstrates a workflow.
You can put everything in the separate mode and provide an FK to in the Process model.
But the state field itself is the antipattern since eventually, you can have several tasks executed in parallel. And even sequential workflow could be constantly changed, new tasks could be added or deleted. You can have only published Boolean or DateTime field in the POST model to filter on that on the front end.
The general rule could be - keep all people workflow decisions in the Process model, and build all data models in a declarative way, keep separated workflow and actual data.

Multiple databases in an app. How to specify per-model

I have several models, one of which uses a database on an external user. The database is defined in the settings file. How would I do the following for this model?
class ItemMaster(models.Model):
id = models.AutoField(primary_key=True) # auto-added anyways
guid = models.CharField(max_length=36, unique=True)
db = using('avails') # <== use this database
Note: the only thing I care about here is being able to do a get_queryset query. I don't need a full database router.
As Shang Wang has pointed out in the comments, using a database router is the best solution.
Whenever a query needs to know which database to use, it calls the
master router, providing a model and a hint (if available). Django
then tries each router in turn until a database suggestion can be
found. If no suggestion can be found, it tries the current _state.db
of the hint instance. If a hint instance wasn’t provided, or the
instance doesn’t currently have database state, the master router will
allocate the default database.
If you are not keen on using routers, one alternative is to call the using on each and every queryset API call to define which database to use.
ItemMaster.objects.using('avails').all()
itemmaster.save(using='avails')
itemmaster.delete(using='avails')
But this will not be very DRY. A second alternative is to create a custom manager and override each of the methods to add the using('avails') fragments. But this involves writing a lot more code that you would write when using a router.

Doctrine2 and database-side triggers for denormalized fields

Let's say I have two tables: Category and Product, and Product links to Category with a foreign key Production.categoryId == Category.id. I would like my database server to take care of counting number of products within a category using a denormalized field Category.productCount - the triggers will update this count on any update/delete/insert, so I don't have to worry about it. Is there a way to synchronize database-side triggers with Doctrine2 entities somehow? I really don't want to recalculate those counters on PHP side, as we are going to run it on multiple servers.
If I understand the question you want to be able to add a new product to a category, persist it then have Category.productCount update itself from the database? You can use
$entityManager->refresh($category);
To reload an entity from the database. I have not done it myself but I would expect that you could use the life cycle functionality to automate this.
But I do kind of wonder if it might not be better to just increment the counter locally without persisting it to the database. Let your trigger do the database operation but, within the request, update the count locally.

Filtering of data according to user logged in Django app

I have a Django app that works well for me, but currently has no notion of user: I am the only one using it, and I would like to change this...
Except for the admin views, the logged-in user should not have access to the data created by other users. There is no shared data between users.
I suppose I have to add a user foreign key to all the models I created. Correct?
Is there a simple way to implement the filtering based on request.user? Can this be done more or less automatically, or do I have to go through all the code to check each and every query done on the database?
I have written the code using TDD, and I intend to follow up... What are the best strategies to ensure that user-filtering is implemented correctly, e.g. that I did not forget to filter an existing query? I suppose I can write tests that show that a particular query is not yet filtered, and implement the filter. But what about the queries that I will write later? Is there a way I can assert that all existing and future queries return objects that only belong to the current user?
Thanks.
Yes, you'll need to add a User FK. Don't forget you'll have to migrate your database tables - either manually, or via a tool like South.
One way of implementing the filter would be to define custom Managers for your models, with a for_user method that takes the User as an argument: something like:
class ForUserManager(models.Manager):
def for_user(self, user):
return self.filter(user=user)
Now you can use this manager - subclassed and/or with a mixin as necessary - on all your models, and remember to use objects.for_user(request.user) everywhere.
This will make testing easier too - your test could monkeypatch that for_user method so that it sets a flag or a counter in a global variable somewhere, and then test that it has incremented as expected.
Edit in response to comment No, as you suspect, that won't work. It's not even that everyone will necessarily get the last-logged-in user: it's that Managers are class-level attributes, and as such are reused throughout a process, so any request served by that server process will use the same one.