Implementing workflows in Django Admin - Django - python

I have a nice admin panel setup so users can manage the data within the site.
Problem is that I need to implement a workflow, so saved models can be approved from and to various stages, to then be finally published.
As the model in question is just one, I thougth of adding a boolean 'approved_for_publishing' field and a 'approved_by' manytomany field.
The obstancle is integrating this within the admin panel.
If someone has a few opinions on the topic that would be really awesome. =)

Some time early I completed similar functionality. Here is what you need to do:
Create an approval status model, and have different variants of approval, i.e each model object represents different approval stage.Also you must have a StatusHistory model which reflects what current status does your article(for example) has.
class Article(models.Model)
title=models.CharField(max_length=32)
body=models.TextField()
class ApprovalStatus(models.Model):
name=models.CharField(max_length=32)
alias=models.CharField(max_length=32,pk=True)
class StatusHistory(models.Model):
status=models.ForeignKey(ApprovalStatus)
article=models.ForeignKey(Article)
current=models.BooleanField(default=True)
So when in your admin you change the status of the article, a new StatusHistory object is created and old object is given current=False variable.
This approach seems a bit bulky, but when you implement it, all you need easily falls into ORM: status history is just a list of all objects, changes to workflow involve only creating new approval status and changing your hardcoded status flow routines

django-werewolf is exactly what you're looking for (https://pypi.python.org/pypi/django-werewolf).
Check the example app here (https://bitbucket.org/barseghyanartur/django-werewolf/src).
For any questions and support, contact me.

Related

Flask - User to Profile Relationships

I'm working with Flask-restplus and I am at a point where I would like to associate each User in my user model to a type of profile, where each user can be associated with one or many profile types. I'm wondering how you guys would go about this. So far, here's what I'm thinking/planning to do. NOTE: I'm not very experienced in web development, so there's a chance I don't know the best way to accomplish this.
Step 1: Create a one-to-many (clients need to also be employees, see below) field (profile_types) relating to a static table that just lists all possible profile options. EXAMPLE:
PK PROFILE TYPE
1 provider
2 employee
3 client
.....
The idea here is to track different information and allow for different views/endpoints for users who are tied to certain profile types. Example, employees would provide a valid login authentication and be directed to page A while a client would be directed to page B, etc. We're also planning on collecting different data points within each profile model.
So an instance of a user might look like this, user1.profile == [client, employee'].
NOTE: This is more important for data collection (ie age of employee, start date, etc) than it is for creating directives based on permissions.
Step 2: Upon creating the new user, a signal fires off the creation of a profile based on the profile_types supplied to the user instance. I've used signals with django in the past, is there a signal library for Flask?
Step 3: An empty profile instance(s) now exists for that user. It will be up to a superuser to upload data in bulk or manually fill out profile information.
Is this a sensible way to go about this? My other though is to create a bunch of Boolean fields in the User model is_provider, is_employee, is_client, etc. All fields that are True get assigned a profile instance. What's the best way to go about this?
Thanks everyone!
Seeing that are you try to validate multiple profile types, you may use
if user.profile_type in ['employee', 'client']
Now, if you want to add an super user I think you can use this
if user.profile_type in ['employee', 'client'] and user.profile_type == 'superuser'
Now, you relationship is more like 'many-to-many', because you are saying that an client also needs to be an employee, if you mean that some endpoints needs to be accessible for employees and clients, then you need to use a 'many-to-one' relationship (an Stackoverflow question which explains what is that)
For your instances, there is Flask Marshmallow, which has an amazing compatibility with Flask SQLAlchemy if you are using an database, but Flask Marshmallow can work alone.

Django: extending user model vs creating user profile model

I'm creating an app in Django and so far I have been using an extended user model like so:
class MyUser(AbstractBaseUser):
...
with all the user and profile info, but I see a lot of people creating different models for the profile and the user itself on stack overflow, using OneToOneField, although those are mostly old questions.
My question is: which is better and, if there isn't a best among them, what are the advantages for each solution?
thanks!
It depends on what you want to do -- if you're happy with the User model as it stands in the latest version of Django you should just use that -- it's easy and you'll get a lot functionality that goes along with it -- for example a pretty good permission system, and you can be sure to be compatible with all third party modules. But if you thing you'll need to expand on the User model, it's pretty straightforward how to do it. You might find that in the future you need to add more methods to your model than you expected.
The examples that you see with separate UserProfile / User model are mostly a legacy of django < 1.5, where that was the recommended way to extend the User model. There's no reason to follow that pattern any more -- it's a lot more work to have to use two models where you just want one model
**2019 Update**
If you are starting a new Django project, you should always create your own custom user model that inherits from AbstractUser, as per the Django documentation, i.e.
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
even if you don't need any additional functionality. The reason for this is that for very low effort, you are making it easy to customize your user object in the future. It's very laborious to replace the built-in User object with your own after you have run the initial migrations, unless you're able to delete all of your data and migrations and start over.
I find some useful information in Django docs:
Extending Django’s default User¶
If you’re entirely happy with Django’s User model and you just want to
add some additional profile information, you could simply subclass
django.contrib.auth.models.AbstractUser and add your custom profile
fields, although we’d recommend a separate model as described in the
“Model design considerations” note of Specifying a custom User model.
AbstractUser provides the full implementation of the default User as
an abstract model.
And:
Model design considerations
Think carefully before handling information not directly related to
authentication in your custom User Model.
It may be better to store app-specific user information in a model
that has a relation with the User model. That allows each app to
specify its own user data requirements without risking conflicts with
other apps. On the other hand, queries to retrieve this related
information will involve a database join, which may have an effect on
performance.
So if I reads it correctly, it means if the fields are related to authentication, then you should consider substitute the original User model. But if it's not related, like profile fields, such birthday, or profile_image, then you might want to create a standalone app that reference the original User model.
And a good tutorial I found: http://riceball.com/d/content/django-18-tutoria-52-adding-user-profile
A ForeignKey is to create a one-to-many relationship. In other words, it will return a queryset. For example, a single car has many wheels, but one wheel isn't attached to several different cars.
A OneToOneField will create a relationship between strictly two objects. For example, the rim belongs to the front-left tire, and only that tire has that rim.
Does that make sense?

Django 1.5 + User model relationship

Django 1.5+ allows us to add custom fields to a User. I want to use this fact, but I don't necessarily know what is good practice. Here is a situation I am confused on how to handle the models.
Given the option to add fields to User, if a project only has one type of User, lets say a Student model, can I simply add student-specific fields to User? I am new to Django, but I believe the alternative would be to set up general User settings, and create a Student model, and a one-to-one unique field in it call user.
Should you ever expand a Django User's fields to mimic that of a model, even if the project is guaranteed only to have one type of user?
If you only have one type of user and are using Django 1.5+, I would recommend taking advantage of the new AbstractUser. Extending Django's default user
As an example where you want to add date of birth and favorite color:
#myusers/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class MyUser(AbstractUser):
dob = models.DateField()
favorite_color = models.CharField(max_length=32, default='Blue')
If you need more flexibility you can extend the AbstractBaseUser instead of AbstractUser, but for most basic cases you should only need AbstractUser.
Also note that in either case, you'll need to reference your user model by using settings.AUTH_USER_MODEL.
Using out example above and assuming the app it was defined in is called myusers:
#settings.py
AUTH_USER_MODEL = 'myusers.MyUser'
The method you mention of creating a Student model with a one-to-one field to the User model still works, but is not as clean (there are still cases where it makes sense if you have multiple kinds of users).
I don't normally like to reference books in answers, but I found that Two Scoops of Django's, Chapter 16 on the User model gave a much clearer explanation of where the different options are appropriate than the current version of the online Django docs. The book is overall a very useful intro to Django and was written based on 1.5. You'd have to buy the book or find someone who has it, though... (FYI: I don't get any money recommending this).
You could also take a look at this SO question/answer: https://stackoverflow.com/a/14104748/307293
You shouldn't touch the django contributed User model (from the authentication framework). This will break upgrades and you do not know what other consequences it might have.
There are two basic ways to do this:
If you just need to store additional information about a user, but don't need to change how the authentication/authorization mechanism works, create a model and add a OneToOneField to the User model. In this model, store any other miscellaneous information.
If you want to change how authentication works you can create your own User model and have django use that (1.5+ only).

Country-based Super User Access, and modifiying Django Auth

I'm looking to give super user access to users of a program I'm devving.
All the entities have a country id value, so i'm just lookign to hook up my user model to have a country ID
Looking at Django Auth, It should be nice and easy to add a super_user_country_id field.
However, how frowned upon is it to modify the core django classes?
Is there any easier way to go about this or?
At the moment, the recommended way is to create a Profile model and link it to the User model with a OneToOneField or a ForeignKey (depending on your requirements). Here's a good tutorial on the topic.
The Django devs have repeatedly expressed their intent to make extending the User model more straightforward, but AFAIK, no concrete design has been proposed, yet.
Adding a custom UserProfile would be one way to go about this. UserProfile can link to Country and you can prevent users based on their UserProfile. I found James Bennett's article on extending the User model useful when I had a similar requirement.
It is generally not a good idea to patch auth (or other built in) classes. Custom patches can become a pain to maintain and keep up to date.

Django ModelForm, having a foreign key as a hidden field

I'm basically building a very trivial form. Let's stick to the books/publisher examples given in the django tutorials and build upon that.
I have a user login to the web app, at which point the first thing they can do is click on a publisher. This publisher then gets saved for their session. Upon that I take them to a create book form. In there I embed the the publisher's id from the database into a hidden field.
Upon the user submitting an HTTP POST, I do something like:
mybookform = BookForm(request.POST)
if mybookform.is_valid():
abook = mybookform.save(commit=False)
abook.publisher_id = request.POST['publisher_id']
mybookform.save()
Yes there's a few naive things done here, such as blindly grabbing the publisher_id and verifying if it's indeed a real publisher id, amongst other security issues. Let's just not pay attention to that for the moment.
My question is, is there a better way of handling this? Although hypothetically this example doesn't make logistical sense, in my particular app the example actually makes sense. The problem is I get a ValueError exception saying publisher_id needs to be a Publisher instance.
Now I can easily retrieve a publisher instance with Publisher.objects.filter(id=..) and use that instead. The question is, is it really necessary? Can I avoid the additional query to the database and somehow update this form instance in a more 'elegant' fashion?
Also, is it possible to somehow embed the publisher in a hidden field so that I do not need to do mybookform.save(commit=False) and just do mybookform = BookForm(request.POST) followed by mybookform.save() immediately?
Retrieving the instance of the publisher does protect against client-side changes that might reference a completely invalid publisher.
To your second question, yes you can include that field as a hidden field by overriding the field in the ModelForm with the approriate form field setting the widget to HiddenInput.
There is no better way to do this.
I would use the get_object_or_404 function for this.
And yes, you can prevent this to be modified by the user by setting the model field to editable=False,

Categories

Resources