I have this postgres function, What it does is when I save an item, it creates an cl_itemid test2021-20221-1. then in table clearing_office the column office_serial increments +1 so next item saved will be test2021-20221-2
SELECT CONCAT(f_office_id,f_sy,f_sem,'-',office_serial) INTO office_lastnumber from curriculum.clearing_office
where office_id=f_office_id;
UPDATE curriculum.clearing_office SET office_serial =office_serial+1 where office_id=f_office_id;
{
"studid": "4321-4321",
"office": "test",
"sem": "1",
"sy": "2021-2022",
}
Is it possible to create this through Django models or perhaps maybe an alternative solution for this problem?
This is my model class
class Item(models.Model):
cl_itemid = models.CharField(primary_key=True, max_length=20)
studid = models.CharField(max_length=9, blank=True, null=True)
office = models.ForeignKey('ClearingOffice', models.DO_NOTHING, blank=True, null=True)
sem = models.CharField(max_length=1, blank=True, null=True)
sy = models.CharField(max_length=9, blank=True, null=True)
class Meta:
managed = False
db_table = 'item'
One simple way might be to use a default function in python.
def get_default_id():
last_id = Item.objects.last().cl_itemid
split_id = last_id.split('-')
split_id[-1] = str(int(split_id[-1])+1)
new_id = '-'.join(split_id)
return new_id
class Item(models.Model):
cl_itemid = models.CharField(..., default=get_default_id)
Similar approach described in this answer.
One thing you will need to anticipate with this approach is that there might be a race condition when two or more new objects are created simultaneously (i.e., the default function runs at the same time for two new objects, potentially resulting in the same ID being returned for the two new objects). You can work around that potential issue with transactions or retries if you think it will be a problem in your application.
A robust approach would be to create your own model field and handle the implementation internally in the field. That would allow you to implement the solution in different ways, depending on the DB dialect and have it work across DB implementations without the race condition issue.
I do this in alot of my Models, tad different. I just use Forms and tac on a custom save method
I haven't worked with unmanaged models, but from what I can see from google it's not different
Form (forms.py)
class ItemForm(forms.ModelForm):
class Meta:
model = Item
fields = (
'cl_itemid',
'studid',
'office',
'sem',
'sy',
)
def __init__(self, *args, **kwargs):
super(ItemForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
obj = super(ItemForm, self).save(commit=False)
if not self.instance:
# you only want it hitting this if it's new!
from datetime import datetime
now = datetime.now()
count = Item.objects.all().count()
# you could also just grab the last
# + chop the id to get the last number
obj.cl_itemid = 'test{0}-{1}'.format(now.strftime('%Y-%m%d%y'), count)
if commit:
obj.save()
You might even be able to tac it BEFORE the actual save method super(ItemForm, self).save(commit=False) or even do a cl_itemid ..i'll do some testing on my end to see if that could be an option, might be much cleaner
Usage
View (POST)
def ItemFormView(request):
from app.forms import ItemForm
if request.method == "POST":
# this is just for edits
itemObj = Item.objects.get(pk=request.POST.get('itemid')) if request.POST.get('itemid') else None
form = ItemForm(request.POST or None, instance=itemObj)
if form.is_valid():
obj = form.save()
return HttpResponseRedirect(reverse('sucesspage')
print(form.errors)
print(form.errors.as_data())
# this will render the page with the errors
data = {'form': form}
else:
data = {'form': ItemForm()}
return render(request, 'itemform.html', data)
Shell
f = ItemForm(data={'office':'test', 'sem':'1', 'etc':'etc'})
if f.is_valid():
o = f.save()
Related
I'm currently creating a Registration-Page with two parts
One part is about the Username and a Passwort.
The second part is about choosing the own PC-Configuration
After defining everything, the User can register to get to the Main-Page.
Therefore I got a Model called "PC_Configuration" with a bunch of Foreign-Keys to the different Database-Models of the Processors/Graphicscards etc.:
class PC_Configuration(models.Model):
user = models.ForeignKey(User, related_name='user_id', on_delete=models.DO_NOTHING)
processor = models.ForeignKey(Processors, related_name='processor_id', on_delete=models.DO_NOTHING)
graphicscard = models.ForeignKey(Graphicscard, related_name='graphicscard_id', on_delete=models.DO_NOTHING)
os = models.ForeignKey(OS, related_name='os_id', on_delete=models.DO_NOTHING)
ram = models.ForeignKey(RAM, related_name='ram_id', on_delete=models.DO_NOTHING)
harddrive = models.ForeignKey(Harddrive, related_name='harddrive_id', on_delete=models.DO_NOTHING)
Also, there is one ForeignKey to the User to connect the Configuration to the respective User-ID.
Inside views.py, I've been creating a DropdownForm for all the Dropdown-Fields which the User shall choose on his own:
class DropdownForm(forms.ModelForm):
class Meta:
model = models.PC_Configuration
exclude = []
def __init__(self, *args, **kwargs):
super(DropdownForm, self).__init__(*args, **kwargs)
self.fields['processors'].queryset = DropdownForm.objects.all()
self.fields['processors'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['graphicscard'].queryset = DropdownForm.objects.all()
self.fields['graphicscard'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['os'].queryset = DropdownForm.objects.all()
self.fields['os'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['ram'].queryset = DropdownForm.objects.all()
self.fields['ram'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['harddrive'].queryset = DropdownForm.objects.all()
self.fields['harddrive'].label_from_instance = lambda obj: "%s" % obj.name
But regarding the fact, that the User-ID shall be assigned to the Configuration automatically, there's no field for that in here.
It is defined in the register_view(request) - Method:
def register_view(request):
form = DropdownForm()
if request.method == "POST":
form = DropdownForm(request.POST)
username = request.POST.get('username')
password = request.POST.get('password')
myuser = User.objects.create_user(username, None, password)
myuser.save()
auth.login(request, myuser)
#form.user = request.user
print(form.errors)
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.save()
messages.success(request, "Account has been created successfully")
return redirect(reverse('gamesearch_view'))
else:
print('Failed')
form = DropdownForm()
render(request, 'register.html', dict(form=form))
return render(request, 'register.html', dict(form=form))
And in here, we got the problem, I guess.
While Testing the Registration, the Testaccounts keep creating and login successfully. But the problem is, that there's no PC-Configuration created because the form is not validating.
With
print(form.errors)
I've been trying to figure out why exactly and it said
<ul class="errorlist"><li>user<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
So it seems like it's necessary to define the "user"-field before checking, if the form is validating and defining the user inside an instance afterwards.
That's why I was trying to do this:
form.user = request.user
But it's still not working and I can't figure out, what's exactly the problem since "user" shouldn't be part of the form-validation.
Can you help me out here?
Thank you in Advance!
You'll have a simpler time with something like this...
Your related_names were somewhat bogus; they're supposed to be the reverse name from the "viewpoint" of the other model. (Also, you never need to add _id to your fields by hand in Django.) If you elide the related_names, they'll implicitly be pc_configuration_set.
on_delete=DO_NOTHING is likely not a good idea. PROTECT is a good default.
It's easier to just handle the username and password as fields in the form.
You were missing exclude = ["user"], so if your template didn't render a field for user, of course it'd be missing. However, you also don't want the POSTer of the form to submit any old user id.
Using a FormView removes most of the boilerplate required to manage forms.
We're using transaction.atomic() to make sure the user doesn't get finally saved to the database if saving the PC Configuration fails.
We assign the created user to form.instance, which is the new but as-of-yet unsaved PC Configuration.
(Of course, imagine these are in separate files.)
from django import forms
from django.db import models, transaction
from django.views.generic import FormView
class PC_Configuration(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT)
processor = models.ForeignKey(Processors, on_delete=models.PROTECT)
graphicscard = models.ForeignKey(Graphicscard, on_delete=models.PROTECT)
os = models.ForeignKey(OS, on_delete=models.PROTECT)
ram = models.ForeignKey(RAM, on_delete=models.PROTECT)
harddrive = models.ForeignKey(Harddrive, on_delete=models.PROTECT)
class RegisterAndConfigurePCForm(forms.ModelForm):
username = forms.CharField(required=True)
password = forms.CharField(required=True, widget=forms.PasswordInput())
class Meta:
model = PC_Configuration
exclude = ["user"] # we'll assign this by hand
class RegisterAndConfigureView(FormView):
form_class = RegisterAndConfigurePCForm
template_name = "register.html"
def form_valid(self, form):
with transaction.atomic():
user = User.objects.create_user(form.cleaned_data["username"], None, form.cleaned_data["password"])
form.instance.user = user # assign user to the to-be-created PC configuration
form.save()
return redirect(reverse("gamesearch_view"))
I have 3 models:
class Node(models.Model):
ID = models.DecimalField(max_digits=19, decimal_places=10)
name = models.CharField(default='node', max_length=32)
connexion = models.CharField(max_length=255)
# Many2one fields | Foreign keys:
firm = models.ForeignKey('firme.Firme', on_delete=models.CASCADE, null=True, blank=True)
class ScheduledAction(models.Model):
date = models.DateTimeField(default=datetime.now, blank=True)
firm = models.ForeignKey('firme.Firme', on_delete=models.CASCADE, null=True, blank=True)
node_ids = models.ManyToManyField(Node)
I want in ScheduledAction form to show, for a selected firm, the list of its related nodes. Normally I should do this by get:
class ScheduledActionForm(forms.ModelForm):
date = forms.DateTimeField()
firm = forms.ModelChoiceField(queryset=Firme.objects.all())
node_ids = forms.ModelMultipleChoiceField(queryset=Node.objects.get(firm_id=firm.id), widget=forms.CheckboxSelectMultiple)
class Meta:
model = ScheduledAction
fields = [
'date',
'firm',
'node_ids'
]
This is my views.py:
def planification_view(request, id):
scheduledAction = ScheduledActionForm(request.POST or None)
firme = get_object_or_404(Firme, id=id)
nodes = Node.objects.all()
if scheduledAction.is_valid():
scheduledAction.save()
print('formulaire enregistre')
scheduledAction = ScheduledActionForm()
context = {
'firme': firme,
'form': scheduledAction,
'nodes': nodes
}
return render(request, "node/planification.html", context)
But I got this error:
AttributeError: 'ModelChoiceField' object has no attribute 'id'
How can I fix this?
There are several things wrong with your approach
first of all: you assigned the variable 'firm' to a form field and then using that same variable to get an object from the db won't work in anyway. The variable 'firm' in Node.objects.get(firm_id=firm.id) should be a firm instance.
second: queryset=Node.objects.get(firm_id=firm.id) won't work because Model.objects.get() will return an object not a query set.
If you want to show all the nodes for a specific Firm you will need to pass the firm object (or id or other identifier) to the form by for example passing it through the get_form_kwargs() if you are using CBV and filtering by it Node.objects.filter(firm_id=firm.id)
Update:
Since you are using function based views there might be a more dry way to do this, but I'm only used to working with CBV so I'll try my best
On the form we will declare a new variable called firme (we will pass this to the form in your view)
class ScheduledActionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.firme = kwargs.pop('firme')
super(ScheduledActionForm, self).__init__(*args, **kwargs)
self.fields['node_ids'].queryset = Node.objects.filter(firm_id=self.firme.id)
date = forms.DateTimeField()
firm = forms.ModelChoiceField(queryset=Firme.objects.all()) # why do you want a list of all firms here? Or do you want a single object?
node_ids = forms.ModelMultipleChoiceField(queryset=Node.objects.none()), widget=forms.CheckboxSelectMultiple)
class Meta:
model = ScheduledAction
fields = [
'date',
'firm',
'node_ids'
]
now for you view:
def planification_view(request, id):
firme = get_object_or_404(Firme, id=id)
nodes = Node.objects.all()
if request.method == 'POST':
form = ScheduledActionForm(data=request.POST, firme=firme)
if form.is_valid():
scheduledAction = form.save()
print('formulaire enregistre')
else:
form = ScheduledActionForm(firme=firme)
context = {
'firme': firme,
'form': form,
'nodes': nodes
}
return render(request, "node/planification.html", context)
Something along these lines should get you going. It might still require some tweaking to get it to work in your project. If you still have trouble understanding please read the Django docs and or do their tutorial
Update 2:
Not the prettiest solution but it works for the OP
I have a model that has class methods. In testing the class methods work and alter the model instances according to my needs. The issue is using this class method in the admin. When an application cannot pay a late payment fee is applied creating another transaction altering the balance. The method in models is decorated with a #classmethod decorator:
class Transactions(models.Model):
application = models.ForeignKey(Application,
related_name='application_id', blank=True, null=True)
transaction_type = models.CharField(max_length=56,
choices=TRANSACTION_TYPE, null=True)
transaction_source = models.CharField(max_length=56,
choices=TRANSACTION_SOURCE, null=True)
transaction_method = models.CharField(max_length=56,
choices=TRANSACTION_METHOD, null=True)
amount = models.DecimalField(max_digits=10, decimal_places=2)
created_date = models.DateField()
posted_date = models.DateField()
external_reference = models.CharField(max_length=100, null=True,
verbose_name='External Reference')
def __unicode__(self):
return self.application.forename + " " +
self.application.surname + "(" + str(self.application.id) + ")"
#classmethod
def late_payment_fee(cls, app, desired_action):
"""
This function enacts a late payment fee to an application as a
transaction
:param app: application the fee is going to be applied to
:param desired_action: either True or False, when reversing the
fee the transaction shouldn't be deleted,
just another transaction of the opposite effect in order to
help loans collection with tracking, True will
enact a feee, False will reverse the fee
:return: a transaction which is stored in the database
"""
today = str(date.today())
if desired_action:
trans_type = MISSEDREPAYMENTFEE
amount = float(12)
else:
trans_type = MISSEDREPAYMENTFEEREVERSAL
amount = float(-12)
cls.create_trasaction(app, trans_type, INTERNALBOOKING,
INTERNALBOOKING, amount, today, today, None)
I need to get it so when status is altered, or when a tickbox is checked in the admin for an application it fires the class method. I have Googled overriding models in admin but cannot find anything. Here's the admin:
class ApplicationAdmin(ImportExportModelAdmin):
resource_class = ApplicationResource
search_fields = ['forename', 'surname']
list_filter = ('status', 'underwritingresult', 'test', 'signed',
'mandateapproved', 'dealership', 'brand')
list_select_related = ('instalment',)
list_display = ('id', 'SPV_ID', 'forename', 'surname'......
inlines = [
InstalmentInline,
AddressInline
]
exclude = ['customer', 'accountnumber', 'sortcode']
readonly_fields = ('Account_Number', 'Sort_Code', 'SPV_ID')
def get_readonly_fields(self, request, obj=None):
readonly_fields = []
if obj.locked :
for field in self.model._meta.fields:
readonly_fields.append(field.name)
else:
readonly_fields = ('Account_Number', 'Sort_Code', 'SPV_ID')
return readonly_fields
def Account_Number(self, obj):
return Decrypt(obj.accountnumber)
def Sort_Code(self, obj):
return Decrypt(obj.sortcode)
def SPV_ID(self, obj):
return obj.spv.id
def has_delete_permission(self, request, obj=None):
return False
Many thanks for reading this.
I've now found the solution (sorry if my initial question was not very clear). It turned out that I needed to override the save function in the Application model. However, another issue was raised when doing this. If a user changed something benign like the name of the customer then the class method would fire and create new transactions even though there was no real change. Because of this, we have to override the init and the save function in the model but still actually save. For this model, we are interested to see if the status has changed or a late fee has been applied. In order to do this, we have to store the initial status values by overriding the init method in the model:
__initial_status = None
__initial_fee_status = None
def __init__(self, *args, **kwargs):
"""
This function overrides the __init__ function in order to
save initial_status facilitating comparisons in the save function
:param args: allows the function to accept an arbitrary number of
arguments and/or keyword arguments
:param kwargs: same as above
"""
super(Application, self).__init__(*args, **kwargs)
self.__initial_status = self.status
self.__initial_fee_status = self.feeadded
Now we have them stored we can pass them through our save function. If there is a change we can utilize the class method:
def save(self, *args, **kwargs):
"""
This function overrides the standard save function. The application
is still saved, however, other functions are fired when it is
saved.
:return: creates reversal commission and dealership transactions
if the status is CANCELLED
"""
if self.signed and self.status != self.__initial_status:
if self.status == Application.CANCELLED:
Transactions.create_commision_trasaction(app=self,
reverse=True)
Transactions.create_dealership_trasaction(app=self,
reverse=True)
elif self.status == Application.OK:
Transactions.create_commision_trasaction(app=self,
reverse=False)
Transactions.create_dealership_trasaction(app=self,
reverse=False)
if self.signed and self.feeadded != self.__initial_fee_status:
if self.feeadded:
Transactions.late_payment_fee(app=self, desired_action=True)
elif self.feeadded is False:
Transactions.late_payment_fee(app=self, desired_action=False)
super(Application, self).save(*args, **kwargs)
Making good progress with my Django project but for this sticking usability issue. I want to be able to filter dropdowns in inline forms based on the parent id. For example.
The Theme model belongs to a course.
class Theme (models.Model):
id = models.AutoField(primary_key=True) # AutoField?
code = models.CharField(max_length=5)
course = models.ForeignKey(Course)
theme_text = models.CharField(max_length=50)
description = models.TextField(blank=True, max_length=1000)
zorder = models.IntegerField()
The indicator model also belongs to a course
class Indicator (models.Model):
id = models.AutoField(primary_key=True) # AutoField?
code = models.CharField(max_length=10)
indicator_text = models.TextField(blank=True)
explained = models.TextField("Explained",blank=True)
course = models.ForeignKey('curriculum.Course')
theme = models.ForeignKey(Theme)
strand = models.ForeignKey(Strand,blank=True,null=True)
level = models.ForeignKey(Level,blank=True,null=True)
concept = models.ManyToManyField(Concept,blank=True)
The admin theme form calls an Indicator Inline
class ThemeAdmin(admin.ModelAdmin):
list_display = ['code', 'theme_text','description','course']
list_filter = ['course']
inlines = [InlineIndicator]
which in turn calls a IndicatorInlineForm
class InlineIndicator(admin.TabularInline):
form = IndicatorInlineForm
model = Indicator
extra = 0
the IndicatorInlineForm filters data to some fields (course, theme, strand and concept)
class IndicatorInlineForm (forms.ModelForm):
def __init__(self, *args, **kwargs):
super(IndicatorInlineForm, self).__init__(*args, **kwargs)
if self.instance.pk is not None:
self.fields['course'].queryset = Course.objects.filter(id=self.instance.course)
self.fields['theme'].queryset = Theme.objects.filter(course=self.instance.course)
self.fields['strand'].queryset = Strand.objects.filter(course=self.instance.course)
self.fields['concept'].queryset = Concept.objects.filter(course=self.instance.course)
else:
self.fields['course'].queryset = Course.objects.filter(id=4)
self.fields['theme'].queryset = Theme.objects.filter(course=4)
self.fields['strand'].queryset = Strand.objects.filter(course=4)
self.fields['concept'].queryset = Concept.objects.filter(course=4)
This is working for existing records where the course id has been set in the indicator record but it is not yet working for new indicator records.
I would like new indicator records to use the course the course.id as per the theme to filter the available options.
I'm not sure I see how the suggested duplicate solves this as it is for existing records or maybe I am wrong in which case help me!
I've been reading about formsets but it feels like it maybe over complicating it. I'm also a bit confused about where it would slot in with what I have already.
Thanks in anticipation of your help (and all the help so far).
Chris
I was able to answer this using formfield-for-foreignkey-and-inline-admin although it does not appear to work for the concept manytomany field.
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
changelist_filters = request.GET['_changelist_filters'].rsplit("=",1)
course_id = int(changelist_filters[1])
if db_field.name == 'course':
kwargs['queryset'] = Course.objects.filter(pk = course_id)
elif db_field.name == 'strand':
kwargs['queryset'] = Strand.objects.filter(course = course_id)
elif db_field.name == 'concept':
kwargs['queryset'] = Concept.objects.filter(course = course_id)
return super(InlineIndicator, self).formfield_for_foreignkey(db_field, request, **kwargs)
I removed the filters from the IndicatorInlineForm as these were no longer required.
I created an app to audit operations on objects. When an object is created, updated or deleted, a new record is created in my audit table (Auditor model).
The Audit app is working now, but to use it on my models requires some work and a lot of code that I believe can be avoided with a most optimized approach.
Which django resources or approach can I use to integrate the Audit app on my models, without write so much code? I need a simplified way to archieve this integration of the Auditor app with all my models and other projects.
I'll give an example how I using the Auditor model with a model (Car) that I want to audit.
Thank you.
Car model
class Car(models.Model):
name = models.CharField(max_length=50)
brand = models.CharField(max_length=50)
color = models.CharField(max_length=50)
is_available = models.BooleanField(default=True)
Auditor model
class Auditor(models.Model):
field = models.CharField(max_length=255, blank=True)
action = models.CharField(max_length=6)
old_value = models.TextField(blank=True)
new_value = models.TextField(blank=True)
stamp = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
content_type = models.ForeignKey(ContentType, blank=True)
object_id = models.PositiveIntegerField(blank=True)
content_object = GenericForeignKey('content_type', 'object_id')
deleted_object = models.CharField(max_length=255, blank=True)
Car view
from audittest.apps.auditor.models import Auditor
from django.contrib.contenttypes.models import ContentType
#Function for audit creation. I know that the view is not the right place to put this function, but I put this here for test.
def create_audit(obj, request, action, obj_id=False):
if action == 'CREATE':
audit = Auditor(content_type = ContentType.objects.get_for_model(obj), object_id = obj.id, user = request.user, action = action)
elif action == 'DELETE':
audit = Auditor(content_type = ContentType.objects.get_for_model(obj), object_id = obj_id, user = request.user, action = action, deleted_object = obj)
audit.save()
def new(request, template_name='cars/form.html'):
form = CarForm(request.POST or None)
if form.is_valid():
obj = form.save()
create_audit(obj, request, 'CREATE')
return redirect('car:admin')
return render(request, template_name, {'form':form, 'title':u'Novo Car'})
def edit(request, pk, template_name='cars/form.html'):
car = get_object_or_404(Car, pk=pk)
form = CarForm(request.POST or None, instance=car, request=request)
if form.is_valid():
form.save()
return redirect('car:admin')
return render(request, template_name, {'form':form,'title':u'Editar Car'})
def delete(request, pk, template_name='cars/confirm_delete.html'):
car = get_object_or_404(Car, pk=pk)
obj_id = car.id
if request.method=='POST':
car.delete()
create_audit(car, request, 'DELETE', obj_id=obj_id)
messages.success(request, u'Car excluĂdo com sucesso.')
return redirect('car:admin')
return render(request, template_name, {'object':car})
Car form
class CarForm(ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(CarForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super(CarForm, self).clean()
# Audit updated fields
if self.instance.pk is not None:
fields = []
for field in self.instance._meta.get_all_field_names():
if field != 'id' and getattr(self.instance, field) != cleaned_data[field]:
#fields.append((field, getattr(self.instance, field), cleaned_data[field]))
audit = Auditor(content_type = ContentType.objects.get_for_model(self.instance), object_id = self.instance.pk, user = self.request.user, action = 'UPDATE', field = self.instance._meta.get_field(field).verbose_name, old_value = getattr(self.instance, field), new_value = cleaned_data[field])
audit.save()
return cleaned_data
Use class based views in your applications so you can use the full advantages of the cool Mixins,
I usually create a Mixin that can be added to any CreateView or UpdateView.
class withAudit(object):
"""
A mixin that will create an audit record wither the action is
Create or Update
"""
def get_success_url(self):
"""
This will be called when the form is valid and saved.
"""
# create the record
audit = Auditor(content_type= ContentType.objects.get_for_model(self.model))
audit.object_id = self.object.pk
audit.user = request.user
# You will need a way to capture this action.
# there are many ways to do it.
audit.action = "Create"
audit.save()
return super(withAudit, self).get_success_url()
In your views you have to use it this way
class CarCreate(withAudit, CreateView):
model = Car
For update
class CarUpdate(withAudit, UpdateView):
model = Car
You can do the following to any UpdateView or CreateView in your application. However, For Deleting the object, I think you will need another mixin which will capture the data before performing the action. You need to see the class based views docs in order to customise these as you want.
The same idea can be done with decorators if you really want keep using method based views.
If you have a big application with high traffic, this process should be done in the background where you define a stack or queue an you keep passing these information to it, which will provide a better performance indeed. Some big applications using another database for logs and audit.