Need help for Django model save() method.
i need a redirection back to Person list page or to Person form if some conditions false on my model save() method .
Currently i used a validation error but i am looking for redirection back to list page or form. And if i use a return statement, always get added successfully without saving.
Here i need a redirection instead of "raise ValidationError(u"Enter a valid name").
Thanks in Advance
Justin
#Models.py
from django.db import models
from django.core.exceptions import ValidationError
class Person(models.Model):
first_name = models.CharField(max_length=30, blank=True)
last_name = models.CharField(max_length=30, blank=True)
address = models.TextField(max_length=300, blank=True)
def save(self, *args, **kwargs):
if self.first_name == "": #(Some condition check, not a simple field value )
raise ValidationError(u"Enter a valid name")
# Need a redirection back to Django Admin Persons list page or to Add Person Page with error message
# But always showing added successfully, if i use a return or redirection.
else:
super(Person,self).save(*args, **kwargs)
#admin.py
from django.contrib import admin
from testapp.models import Person
from testapp.forms import PersonAdminForm
from django.contrib import messages
# Register your models here.
class PersonAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name')
#form = PersonAdminForm
#def save_model(self, request, obj, form, change):
# obj.user = request.user
# obj.save()
# messages.add_message(request, messages.INFO, 'Text of message')
admin.site.register(Person, PersonAdmin)
#forms.py
from testapp.models import Person
from django import forms
class PersonAdminForm(forms.ModelForm):
class Meta:
model = Person
Thanks friends for the reply,
I adding my actual model class here. actually i am not using any custom form or custom validation. i used only django admin interface. But i added a custom save() method in model class. And in that save() method i did some conditions on edit and add cases.
Addind data and editing data working with save() method correctly. But condition false case we have no option redirect back to admin model class listing or admin form with error message?
Here in my example can i use any other code instead of raise ValidationError("error test message")?. raise ValidationError gives django error page. if i use redirect or return give "... added successfully" message on no data saving also.
Any chance?
my code ...
class Asset(models.Model):
-----code--------
class Meta:
verbose_name_plural= "Employees Assets"
def save(self, *args, **kwargs):
----- code------
if self.pk is not None:
++++++++++ some code +++++++++++
if self.hardware.hardware_status == 0 and edit_flag == 2:
++++++++++ some code +++++++++++
elif self.hardware.hardware_status == 1 and edit_flag == 1:
++++++++++ some code +++++++++++
elif (self.hardware.hardware_status == 0 or self.hardware.hardware_status == -1) and edit_flag == 1:
++++++++++ some code +++++++++++
elif self.hardware.hardware_status == -1 and edit_flag == 2:
raise ValidationError('Cant modify Hardware, Hardware status is Lost ')
else:
raise ValidationError('Cant modify Hardware, Hardware already assigned to other staff')
self.hardware.save()
super(Asset, self).save(*args, **kwargs)
else:
if self.hardware.hardware_status == 0:
++++++++++ some code +++++++++++
else:
raise ValidationError(u'Can't assign, Hardware not available(Lost/Alreday Assigned) for assignment')
self.hardware.save()
super(Asset, self).save(*args, **kwargs)
def __unicode__(self):
return u'%s Hardware information for %s' % (self.hardware, self.employee)
That is completely the wrong place to do it. Models, deliberately, do not know anything about the request. There's no way to redirect from a save method, and you should not try to implement one. Your view is responsible for running validation and redirecting as approrpriate.
try to custom validation in admin:
forms.py:
class PersonAdminForm(forms.Form):
class Meta:
model = Person
def clean_first_name(self):
if self.first_name == "":value )
raise ValidationError(u"Enter a valid name")
else
return self.clean_first_name["first_name"]
admin.py:
class PersonAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name')]
form = PersonAdminForm
check this linkvalidation
The model doesn't do redirection. Which means, overriding your model save method is of little use here. This is a very direct case of form validation, therefore, you must use the below in forms.py
Your view is taking care of the form redirection for you, therefore, just write your code in the form.
Related
Models.py
class user(AbstractUser):
salary_number = models.IntegerField(unique=True, null=True, blank=True)
Admin.py
def save_model(self, request, obj, form, change):
if obj.salary_number == None:
raise ValidationError("ERROR salary number!")
....
obj.save()
I'm trying to show error for user if they forget to fill salary number, but I got error ValidationError at /admin/information/user/add/. How can I fixed that?
I have a reason to not set salary_number null & blank = False.
The simplest way is probably to make a small ModelForm:
from django import forms
from django.core.exceptions import ValidationError
class UserModelForm(forms.ModelForm):
def clean_salary_number(self):
data = self.cleaned_data['salary_number']
if data is None:
raise ValidationError('ERROR salary number!')
return data
and then plug this in in the ModelAdmin:
from django.contrib import admin
class MyModelAdmin(admin.ModelAdmin):
# …
form = UserModelForm
Using Django 2.x, I have a Profile model, and I want to update some User fields in the Profile's UpdateView, for example, User.email
I can get the email field to display, and to show the current value, but I can't figure out how to get it to update.
Here's my setup:
models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
# other fields
forms.py:
class UserForm(forms.ModelForm):
class Meta:
model=User
fields=('email')
class ProfileForm(forms.ModelForm):
class Meta:
model=Profile
fields=(...)
views.py
class ProfileUpdate(UpdateView):
model=Profile
form_class = ProfileForm
user_form_class = UserForm
def get_context_data(self, **kwargs):
...
user = get_object_or_404(Profile, pk=self.kwargs.get('pk')).user
context['user_form']= self.user_form_class(instance=user)
Everything up to here seems to work. The user form works and the current email address appears in the form. However, this is where I get stuck, I don't know how to save that email address.
Still within the ProfileUpdate, this is what I'm trying:
def post(self, request, *args, **kwargs):
user_form = self.user_form_class(request.POST)
if user_form.is_valid():
user_form.save()
# code makes it this far when submitting form.
return super(ProfileUpdate, self).post(self, request, *args, **kwargs)
Although the code is run, only the Profile model is updated, the User model doesn't get an updated email address.
Give django-shapeshifter a try: https://github.com/kennethlove/django-shapeshifter
pip install django-shapeshifter
The example in the README does exactly what you are looking for. Full disclosure, I'm a contributor to the project.
Good luck!
I'm using Django-Rest-Framework(ViewSet approach) on my project interacting with a React app. So, I'm not using Django admin nor Django forms.
My project's structure is:
View
Serializer
Model
What I need to do is to perform actions before models method calls:
Insert the request.user on a Model field.
Start a printer process after a Model.save()
.....
I have read a lot about django-way to do on Django.docs and there, the things seems to be showed for a Django-Admin like project, which is not my case.
By other hand, by reading the Stack's answers about in other topics, the way to do seems to be like: "It will work, but, It's not the right way to do that".
According to Django's documentation, the best way to perform that supposed to be by using a new file, called admin.py, where I would to register actions binding to a Model which could support save, delete, etc., but, it's not clear if this approach is to do that or only for provide a Django-Admin way to perform an action.
# app/models.py
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
user = models.ForeignKey(User)
content = models.TextField()
class Comment(models.Model):
post = models.ForeignKey(Post)
user = models.ForeignKey(User)
content = models.TextField()
# app/admin.py
from app.models import Post, Comment
from django.contrib import admin
class CommentInline(admin.TabularInline):
model = Comment
fields = ('content',)
class PostAdmin(admin.ModelAdmin):
fields= ('content',)
inlines = [CommentInline]
def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()
def save_formset(self, request, form, formset, change):
if formset.model == Comment:
instances = formset.save(commit=False)
for instance in instances:
instance.user = request.user
instance.save()
else:
formset.save()
admin.site.register(Post, PostAdmin)
According to the answers I have heard, the best way would use something like that on Models:
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
return super(MyModelForm, self).__init__(*args, **kwargs)
def save(self, *args, **kwargs):
kwargs['commit']=False
obj = super(MyModelForm, self).save(*args, **kwargs)
if self.request:
obj.user = self.request.user
obj.save()
return obj
What I want to know is:
What's the best way to to perform that actions, on which files, what's the best structure.
to insert a request.user on a Model field you can use the perform_create() method on your view class. for more information visit https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/#associating-snippets-with-users which is exactly what u want!
I'm not sure what you mean by start a printer process, but you usually can override save() method on your model class for doing a process after saving a model instace.
https://www.django-rest-framework.org/api-guide/serializers/#overriding-save-directly
The best way I found to insert the request.user on the model, as a "created_by" field, was by inserting a hidden field on the model serializer with a default data, just like these:
my_field = serializers.HiddenField(default=serializers.CurrentUserDefault())
The CurrentUserDefault() is a function wich returns the user request onto serializer.
https://www.django-rest-framework.org/api-guide/validators/#advanced-field-defaults
For actions performing after/before a save/delete, what I chose to use Django Signals,wich works as a dispatcher of actions, a little like react's redux.
https://docs.djangoproject.com/en/2.2/topics/signals/
Thank you everybody for the helpful answers.
I want to restrict other user to change object instance other then owner user.
For that I am using has_change_permission admin model function but It is not working.
My model:
class Book(models.Model):
author = models.ForeignKey(User, related_name = 'book_author')
...
In my admin.py
class BookAdmin(admin.ModelAdmin):
def has_change_permission(self, request, obj):
if request.user.is_super_user():
return True
elif request.user == obj.author:
return True
else:
return False
if obj is None:
return False
In my views.py
class BookUpdate(generic.UpdateView):
model = Book
form_class = BookUpdateForm
template_name = 'accounts/book_update.html'
def get_object(self, *args, **kwargs):
return Book.objects.get(id=self.kwargs.get('id'))
In my urls:
url(r'^update_book/(?P<id>[\w-]+)/$', views.BookUpdate.as_view(),name='update_book')
Now when any one goes to this url pattern can edit book, but I need that only author can edit this book.
Is has_change_permission right way to do so, or any other better way ?
This will only work in the Django admin interface. It looks like you're trying to update your Book instance in a custom form. You can for example overwrite the save() method of your model and check for permission here. See this part of the doc.
Say I'm writing a multi-blog application and I want each author to use unique titles for their articles (but unique per user, not globally unique):
class Article(models.Model):
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=255)
#[...]
class Meta:
unique_together = (('title', 'owner'),)
Now, I want the author field to be auto-filled by the application:
class ArticleAdmin(ModelAdmin):
exclude = ('owner',)
def save_model(self, request, obj, form, change):
if not change:
obj.owner = request.user
obj.save()
Actually this does not work: if I try to create a new Article with an existing author-title combination, Django will not check the uniqueness (because author is excluded from the form) and I'll get an IntegrityError when it hits the database.
I thought of adding a clean method to the Article class:
def clean(self):
if Article.objects.filter(title=self.title, owner=self.owner).exists():
raise ValidationError(u"...")
But it seems that Article.clean() is called before ArticleAdmin.save_model(), so this does not work.
Several variants of this question have been asked already here, but none of the solutions seem to work for me:
I cannot use Form.clean() or other form methods that don't have the request available, since I need the request.user.
For the same reason, model-level validation is not possible.
Some answers refer to class-based views or custom views, but I'd like to remain in the context of Django's Admin.
Any ideas how I can do this without rewriting half of the admin app?
You are finding a way to bring request to customized form, in ModelAdmin, actually:
from django.core.exceptions import ValidationError
def make_add_form(request, base_form):
class ArticleForm(base_form):
def clean(self):
if Article.objects.filter(title=self.cleaned_data['title'], owner=request.user).exists():
raise ValidationError(u"...")
return self.cleaned_data
def save(self, commit=False):
self.instance.owner = request.user
return super(ArticleForm, self).save(commit=commit)
return ArticleForm
class ArticleAdmin(admin.ModelAdmin):
exclude = ('owner',)
def get_form(self, request, obj=None, **kwargs):
if obj is None: # add
kwargs['form'] = make_add_form(request, self.form)
return super(ArticleAdmin, self).get_form(request, obj, **kwargs)