call a method in Django admin site - python

i have a Django project and right now everything works fine. i have a Django admin site and now, i want that when i add a new record to my model, a function calls simultaneously and a process starts. how i can do this? what is this actions name?

1 WAY
You can go to your models.py into your app by using django signal you can do this.
from django.db.models.signals import post_save
class Test(models.Model):
# ... fields here
# method for updating
def update_on_test(sender, instance, **kwargs):
# custome operation as you want to perform
# register the signal
post_save.connect(update_on_test, sender=Test)
2 WAY
You can ovveride save() method of modeladmin class if you are filling data into table by using django admin.
class TestAdmin( admin.ModelAdmin ):
fields = ['title', 'body' ]
form = TestForm
def save_model(self, request, obj, form, change):
# your login if you want to perform some comutation on save
# it will help you if you need request into your work
obj.save()

Related

How to limit the inline ForeignKey queryset to the instance itself in Django admin

I have 5 models and their relations are as follows:
class A(models.Model):
pass
class B(models.Model):
a = models.ForeignKey(A)
class C(models.Model):
b = models.ManyToManyField(B)
class D(models.Model):
pass
class I(models.Model):
a = models.ForeignKey(A)
b = models.ForeignKey(B)
c = models.ForeignKey(C)
d = models.ForeignKey(D)
I decide to use the django admin
class IAdminInline(admin.TabularInline):
pass
class DAdmin(admin.ModelAdmin):
inlines = [IAdminInline, ]
The admin page makes a lot queries,if too many I instances are related to D, which is time consuming. So I disable the Django default actions by setting the formfield_for_foreignkey:
def formfield_for_foreignkey(self, db_field, request, **kwargs):
field = super().formfield_for_foreignkey(db_field, request, **kwargs)
field.choices = [] # django will not make any queries if set choices
Instead, I use ajax to get the corresponding data, and use javascript to render the select widgets and bind actions , which make it easier to add data since these widgets are related to each other. Page loads faster but problem is that, the above code would clear I instances initial values that are apparently already existing in the change view page.
I want to ask how can I render the existing inline object select widgets to their own values? Does Django provide any functions to handle this?
I haven't find any solutions other than using ajax to render the apparently existing values all by myself.

How to create a new item in the database upon opening Django CreateView

I'm trying to auto-create an object in database in the background, upon opening a link in Django using Django generic class views, here is my code :
class BuyInvoiceItemCreateView(generic.CreateView):
model = models.BuyInvoiceItems
fields = "__all__"
def get(self, request, *args, **kwargs):
models.BuyInvoice.objects.create(number=0)
return render(request, "Point_of_sale/buyinvoiceitems_form.html")
I've tried to override the get method, but it creates 2 objects every time I open the link instead of 1 object.

How to save related model instances before the model instance in django?

How to save the related model instances before the instance model.
This is necessary because I want to preprocess the related model's instance field under model instance save method.
I am working on Django project, and I am in a situation, that I need to run some function, after all the related models of instance get saved in the database.
Let say I have a model
models.py
from . import signals
class Video(models.Model):
"""Video model"""
title = models.CharField(
max_length=255,
)
keywords = models.ManyToManyField(
KeyWord,
verbose_name=_("Keywords")
)
When the new instance of video model is created.
I need to
1. All the related models get saved first.
a. If the related models are empty return empty or None
2. and then Save this video instance.
I tried to do it using post_save signals, but couldn't succeed as there is no guarantee that related models get saved first that the model.
from django.db.models.signals import post_save, pre_delete, m2m_changed
from django.dispatch import receiver
from .models import Video
#receiver(m2m_changed, sender=Video)
#receiver(post_save, sender=Video)
def index_or_update_video(sender, instance, **kwargs):
"""Update or create an instance to search server."""
# TODO: use logging system
# Grab the id
print("Id is", instance.id)
# Keywords is empty as keyword instance is saved later than this instace.
keywords = [keyword.keyword for keyword in instance.keywords.all()]
print(keywords) # [] empty no keywords
instance.index()
#receiver(pre_delete, sender=Video)
def delete_video(sender, instance, **kwargs):
print("Delete index object")
instance.delete()
Update:
Can be implemented by grabbing the post_save signals and wait unitls
its related models get saved in db, when the related_models get saved
start serialization process and create flat json file along with the models fields and its related instance so, the flat json file can index
into elastic search server.
And the question aries, how much time should we wait in signal handler method? and how to know all instance related fields got saved in db.
class Video(models.Model):
def save(self, *args, **kwargs):
# 1. Make sure all of its related items are saved in db
# 2. Now save this instance in db.
# 3. If the model has been saved. Serialize its value,
# 4. Serailize its related models fields
# 5. Save all the serialized data into index server
# The advantage of using this is the data are indexed in real
# time to index server.
# I tired to to implement this logic using signals, in case of
# signals, when the instance get saved, its related models are
# not instantly available in the databse.
# Other solution could be, grab the `post_save` signals, wait(delay
# the serialization process) and start the serialization of
# instance model and it's related to convert the data to flat json
# file so, that it could index in the searching server(ES) in real
# time.
# until the instance related models get saved and start to
# serialize the data when its
By the way I am using django-admin and I am not defining the logics in
a view, adding the related model instance is handled by django admin
In this case, you can flip the order with which ModelAdmin calls save_model() and save_related() so from Model.save() you will be able to reach the updated values of the related fields, as stated in this post.
class Video(models.Model):
def save(self, *args, **kwargs):
if not self.id:
super().save(*args, **kwargs)
all_updated_keywards = self.keywards.all()
...
super().save(*args, **kwargs)
class VideoAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
if not obj.pk:
super().save_model(request, obj, form, change)
else:
pass
def save_related(self, request, form, formsets, change):
form.save_m2m()
for formset in formsets:
self.save_formset(request, form, formset, change=change)
super().save_model(request, form.instance, form, change)
You can override model's save() method and save related models (objects) before saving instance.

Django FormPreview: Save form data to database

Probably a simple question but having trouble implementing a form preview page using django-formtools. I've configured everything per the docs. I'm stuck on what to add to the done() method to save the data to db.
forms.py
class JobForm(ModelForm):
class Meta:
model = Job
fields = ('title', 'category', 'company', 'website', 'description',)
class JobFormPreview(FormPreview):
def done(self, request, cleaned_data):
# add what here to save form data as object?
return HttpResponseRedirect('/success')
urls.py
...
url(r'^jobs/new/$',
JobFormPreview(JobForm),
name='job_form'),
...
Using the default templates. The form and preview both render fine, but obviously data doesn't save on submit. Tried self.form.save() per this answer but get an error save() missing 1 required positional argument: 'self'.
I appreciate any guidance.
Looking at the formtools code, it looks as if self.form is the form class, not the validated form instance. Therefore self.form.save() will not work. I've removed the suggestion to call self.form.save() from the linked answer.
I can't see a straight forward way to access the validated form in the done method. I suggest that you create the instance using the cleaned_data instead:
class JobFormPreview(FormPreview):
def done(self, request, cleaned_data):
job = Job.objects.create(**cleaned_data)
return HttpResponseRedirect('/success')

Django Admin - how to prevent deletion of some of the inlines

I have 2 models - for example, Book and Page.
Page has a foreign key to Book.
Each page can be marked as "was_read" (boolean), and I want to prevent deleting pages that were read (in the admin).
In the admin - Page is an inline within Book (I don't want Page to be a standalone model in the admin).
My problem - how can I achieve the behavior that a page that was read won't be deleted?
I'm using Django 1.4 and I tried several options:
Override "delete" to throw a ValidationError - the problem is that the admin doesn't "catch" the ValidationError on delete and you get an error page, so this is not a good option.
Override in the PageAdminInline the method - has_delete_permission - the problem here -it's per type so either I allow to delete all pages or I don't.
Are there any other good options without overriding the html code?
Thanks,
Li
The solution is as follows (no HTML code is required):
In admin file, define the following:
from django.forms.models import BaseInlineFormSet
class PageFormSet(BaseInlineFormSet):
def clean(self):
super(PageFormSet, self).clean()
for form in self.forms:
if not hasattr(form, 'cleaned_data'):
continue
data = form.cleaned_data
curr_instance = form.instance
was_read = curr_instance.was_read
if (data.get('DELETE') and was_read):
raise ValidationError('Error')
class PageInline(admin.TabularInline):
model = Page
formset = PageFormSet
You could disable the delete checkbox UI-wise by creating your own custom
formset for the inline model, and set can_delete to False there. For
example:
from django.forms import models
from django.contrib import admin
class MyInline(models.BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(MyInline, self).__init__(*args, **kwargs)
self.can_delete = False
class InlineOptions(admin.StackedInline):
model = InlineModel
formset = MyInline
class MainOptions(admin.ModelAdmin):
model = MainModel
inlines = [InlineOptions]
Another technique is to disable the DELETE checkbox.
This solution has the benefit of giving visual feedback to the user because she will see a grayed-out checkbox.
from django.forms.models import BaseInlineFormSet
class MyInlineFormSet(BaseInlineFormSet):
def add_fields(self, form, index):
super().add_fields(form, index)
if some_criteria_to_prevent_deletion:
form.fields['DELETE'].disabled = True
This code leverages the Field.disabled property added in Django 1.9. As the documentation says, "even if a user tampers with the field’s value submitted to the server, it will be ignored in favor of the value from the form’s initial data," so you don't need to add more code to prevent deletion.
In your inline, you can add the flag can_delete=False
EG:
class MyInline(admin.TabularInline):
model = models.mymodel
can_delete = False
I found a very easy solution to quietly avoid unwanted deletion of some inlines. You can just override delete_forms property method.
This works not just on admin, but on regular inlines too.
from django.forms.models import BaseInlineFormSet
class MyInlineFormSet(BaseInlineFormSet):
#property
def deleted_forms(self):
deleted_forms = super(MyInlineFormSet, self).deleted_forms
for i, form in enumerate(deleted_forms):
# Use form.instance to access object instance if needed
if some_criteria_to_prevent_deletion:
deleted_forms.pop(i)
return deleted_forms

Categories

Resources