Do a certain backend operation on clicking Django Admin save button - python

In a library system, I have the Userbooks (refers to books issued by a user) object registered with Django Admin. And when Django Admin creates a Userbooks object and saves it, the Book object (refers to books in library, also registered with Django Admin) associated with that UserBook with a one_to_one relationship, needs to have its boolean field 'is_issued' to be set to true. How can I do this backend action when Admin clicks the 'save' button?

My suggestion would be to either you use pre save signals or just override the save method to do whatever operation use want
class ModelB(models.Model):
def save(self):
# add logic to change is_issue value to True
super(ModelB, self).save()
Hope this helps.

In the question you specifically asked that this action should happen when the admin tries to save it from admin. The solution suggested by #pansul-bhatt does the same thing on Model save. Even the alternative(Handling pre-save signal) would do the same thing. So even if you save the model from anywhere else in the code you will set is_issued as True.
The better way to do it is to override the save_model on the UserbooksAdmin.
class UserBookAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.is_issued = True
obj.save()
This should be enough to solve your problem. But there are other hooks available with Django Admin.

Related

How to make a "create-only" non-editable field in django admin

im looking for some solution about make a "create-only" field on django admin using models.
I saw some questions before, but no one can answer the core question: the field should appear when the user are creating on admin panel, but i dont want to be able to edit.
models.py
class Fonte(Always):
source_slug = models.CharField(max_length=255)
admin.py
#admin.register(Fonte)
class FonteAdmin(admin.ModelAdmin):
readonly_fields = ['source_slug']
the "readonly_fields" solves the problem in the matter of editing in the future, but ends up forbidding when creating.
Problem: Im using this field to make a hash and i dont wanna this change evermore.. I thought about using a second field that would generate a hash on top of the editable one field in the creation, after that the field would be "dead", but that seems to me to be contrary to the 2 way of normalization.
Is there any more elegant way?
Override get_readonly_fields in your Admin class like that:
def get_readonly_fields(self, request, obj=None):
if obj:
return ['source_slug']
return []

Django REST framework checks unnecessary permissions

Am I using django rest framework (v3.4.6) object level permissions. However, I cannot figure out a few things. First I created a custom permission that checks if user works for a specific shop:
class Works4Shop(BasePermission):
def has_object_permission(self, request, view, obj):
profile = request.user.profile
if obj.shop in profile.shops.all():
return True
else:
return False
I then added permission_classes = (Works4Shop,) to a new custom view class ShopItemsView(APIView)
Now starts the curious part first I read that I need to explicitly check for object level permissions by calling self.check_object_permissions(request,obj). However what I see is that getting any object through model manager enforces the policy on retrieved objects. Well not exactly, it does call the has_object_permission(self, request, view, obj) but it ignores the result. The issue is the performance, this kind of thing creates to many unnecessary selects to DB. Can anyone explain this? I can also post logs from the DB.
So the answer was more simple than I thought. Basically this problem only occurs when using the browsable API. During rendering of the template there are many template tags for forms that use each kind of request specified in the View class (post,put,delete, etc.) and the object permissions is checked for each form individually. When I used pure json format everything started to work as it should, no unnecessary checks etc.
There is however one issue. The browsable api checks permissions for every object fetched which in turn creates an issue: you don't know what is the class of obj in has_object_permission(self, request, view, obj) so you should make an explicit check or the APIView will throw a TypeError

Django - Login User without model

I am trying to implement an SSO login, deriving all the authorization rights from saml response:
class SAMLServiceProviderBackend(object):
def authenticate(self, saml_authentication=None):
if not saml_authentication: # Using another authentication method
return None
if saml_authentication.is_authenticated():
attributes = saml_authentication.get_attributes()
user = User(username=saml_authentication.get_nameid())
user.first_name = attributes['givenName']
user.last_name = attributes['sn']
return None
in views I got something like
...
user = authenticate(saml_authentication=auth)
login(self.request, user)
...
login fails because of the missing save() method. The only way would be to inherit from User and override the save method. Trying this, I got the next errors with is_authenticated, get_and_delete_messages, and so on
Is there an easy way to insert a user object into session, without saving the user to database?
Something like:
request.session['user'] = authenticate(saml_authentication=auth)
I guess should be possible with some limitations, eg. you cannot save your data with a user being a FK.
I have tried this myself, I suspect that you can dynamically create a user instance in the authenticate() method just don't call user.save(), or overwrite the save() method to do nothing.
You might also need to hook up the user record between requests, so you might need to write your own serializer for the user and load that construct the user instance back from session.
I'm fairly certain that Django's session objects are required for the authentication backend. i.e. if you login, then Django needs to store a reference to this fact somewhere. Django generally uses it's database to do this, however, if you're not using a database with your app then you can look at caching sessions instead:
https://docs.djangoproject.com/en/1.11/topics/http/sessions/#using-cached-sessions
(I'm assuming you're not using a database judging by your question)
More importantly however, depending on your needs, you may need to look at creating / configuring, a custom User Model in order to get your backend to work:
https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#substituting-a-custom-user-model

Should I place custom registration code in Views, Models or Managers?

I'm rolling my own custom registration module in Django based on django.contrib.auth. My registration module will have some extra functionality and help me reduce my dependency on other django modules that I'm currently using like django-registration and django-emailchange. I've run into a what-the-best-way-to-do-it problem here.
Note: All the user accounts are based on django.contrib.auth.models.User model.
When the user clicks the "sign-up" link, the request gets passed to my view named register. I have a custom form which has four fields — username, email, password1 and password2. The form is based on django.forms.Form. The form provides basic validation e.g. passoword1 and password2 are the email; the email/username do not exist.
When the data gets POSTed back to my register view, I call the is_valid() method of the form, after which, I create a new user by calling a Manager method called create_user() in django.contrib.auth.models.UserManager. I need to add more custom functionality at this point like sending activation emails, etc. As a best-practice method, where should this logic be? Should this be in a method of the User Model? Should it be where it is currently - the Manager of the Model? Or should this be placed into a custom save() method of my sign-up Form?
Thanks.
Different from Chris, I believe on the philosophy of fat models, thin views.
The more code you can factor inside models, the more reusable your codebase is. Views concerns should be simply managing the request/response cycle and dealing with GET/POST parameters.
In this case, sending activation emails is related to the event of creating a new User. For those cases, Django already provides this abstraction through signals.
http://docs.djangoproject.com/en/1.2/topics/signals/#topics-signals
So, as an example, you can have in your models.py:
from django.contrib.models import User
from django.db.models.signals import post_save
def send_welcome_email(self):
# Reusable email sending code
User.send_welcome_email = send_welcome_email
def welcome_emails(sender, instance, created, **kwargs):
if created:
instance.send_welcome_email() # `instance` is User
post_save.connect(welcome_emails, sender=User)
Similarly, you can have this for when a User is deleted, or everytime a User is saved, etc. Signals are a good abstraction to event-driven tasks.
My recommendation is to not re-solve a problem that django-registration solves quite nicely. It has a pluggable backend system that lets you customize it as much or as little as needed.

Django - How to prepopulate admin form fields

I know that you can prepopulate admin form fields based on other fields. For example, I have a slug field that is automatically populated based on the title field.
However, I would also like to make other automatic prepopulations based on the date. For example, I have an URL field, and I want it to automatically be set to http://example.com/20090209.mp3 where 20090209 is YYYYMMDD.
I would also like to have a text field that automatically starts with something like "Hello my name is author" where author is the current user's name. Of course, I also want the person to be able to edit the field. The point is to just make it so the user can fill out the admin form more easily, and not just to have fields that are completely automatic.
I know that you can prepopulate some values via GET, it will be something like this
http://localhost:8000/admin/app/model/add/?model_field=hello
I got some problems with date fields but, maybe this could help you.
I recently used Django's ModelAdmin.get_form method for this purpose.
class MyModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['my_field_name'].initial = 'abcd'
return form
Yout should be careful about side effects as you are manipulating the base_fields directly.
Django's built-in prepopulated_fields functionality is hardcoded to slugify, it can't really be used for more general purposes.
You'll need to write your own Javascript function to do the prepopulating. The best way to get it included in the admin page is to include it in the inner Media class of a custom Form or Widget. You'll then need to customize your ModelAdmin subclass to use the custom form or widget. Last, you'll need to render some inline Javascript along with each prepopulated field to register the onchange handler and tell it which other field to populate from; I would render this via the custom Widget. To make it nice and declarative you could use a custom ModelAdmin attribute (similar to prepopulated_fields), and override ModelAdmin.formfield_for_dbfield to create the widget and pass in the information about what field it should prepopulate from.
This kind of admin hacking is almost always possible, but (as you can tell from this convoluted summary) rarely simple, especially if you're making an effort to keep your code nicely encapsulated.
I tried a few of these answers and none of them worked. I simply wanted to prepulate a field with another field from a related model. Taking this answer as a starting point, I finally tried to manipulate the model instance object (here obj) directly and it worked for me.
class MyModelAdmin(models.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
if not obj.some_model_field:
obj.some_model_field = obj.related_model.prepopulating_model_field
return form
You can override the default django admin field by replacing it with a form field of your choice.
Check this :
Add custom validation to the admin
I would also like to have a text field
that automatically starts with
something like "Hello my name is
author".
Check out the docs at: http://docs.djangoproject.com/en/dev/ref/models/fields/#default
You could have a CharField() or TextField() in your model, and set this option, which will set the default text. 'default' can also be a callable function.
Something like:
models.CharField(max_length=250, default="Default Text")
The slug handling is done with javascript.
So you have to override the templates in the admin and then populate the fields with javascript. The date thing should be trivial, but I dont know how you should get the logged in users name to the script (not that I have thought very hard but you get the drift :).

Categories

Resources