Django - Last Updated By and Update Date not working - django

Hi I need someone's advice.
I created a form add organizations and a form to edit organizations. These are working fine.
However, I have Last Updated By and Update Date fields on the model - These are not picking up the datetime and user information when editing the organization.
views.py
#login_required()
def organization_edit(request, pk):
org = Organization.objects.get(org_id=pk)
form = OrganizationEditForm(instance=org)
# Update Org
if request.method == 'POST':
form = OrganizationEditForm(request.POST, instance=org)
if form.is_valid():
form.organization_code = form.cleaned_data['organization_code']
form.company_name = form.cleaned_data['company_name']
form.legal_name = form.cleaned_data['legal_name']
form.business_registration_no = form.cleaned_data['business_registration_no']
form.vat_registration_no = form.cleaned_data['vat_registration_no']
form.industry_distribution = form.cleaned_data['industry_distribution']
form.industry_education = form.cleaned_data['industry_education']
form.industry_healthcare = form.cleaned_data['industry_healthcare']
form.industry_manufacturing = form.cleaned_data['industry_manufacturing']
form.industry_retail = form.cleaned_data['industry_retail']
form.industry_services = form.cleaned_data['industry_services']
form.effective_start_date = form.cleaned_data['effective_start_date']
form.effective_end_date = form.cleaned_data['effective_end_date']
org = form.save(commit=False)
org.last_updated_by = request.user
org.save()
return redirect('organizations_settings')
context = {
'form':form,
'org':org,
}
return render(request, 'settings/edit_organization.html', context)
models.py
class Organization(models.Model):
org_id = models.CharField(primary_key=True, max_length=7, default=org_id_generate, editable=False)
organization_code = models.CharField(max_length=20)
company_name = models.CharField(verbose_name="Company Name", max_length=60)
legal_name = models.CharField(verbose_name="Legal Name", max_length=100)
industry_distribution = models.BooleanField(verbose_name="Distribution", default=False)
industry_education = models.BooleanField(verbose_name="Education", default=False)
industry_healthcare = models.BooleanField(verbose_name="Healthcare", default=False)
industry_manufacturing = models.BooleanField(verbose_name="Manufacturing", default=False)
industry_retail = models.BooleanField(verbose_name="Retail", default=False)
industry_services = models.BooleanField(verbose_name="Services", default=False)
business_registration_no = models.CharField(verbose_name="Business Registration Number", max_length=15, blank=True)
vat_registration_no = models.CharField(verbose_name="VAT Registration Number", max_length=15, blank=True)
created_date = models.DateTimeField(default=datetime.now)
created_by = models.ForeignKey(User, on_delete=models.DO_NOTHING, related_name="Created_By", verbose_name="Created By")
effective_start_date = models.DateField(auto_now_add=False)
effective_end_date = models.DateField(auto_now_add=False, blank=True, null=True)
update_date = models.DateTimeField(default=datetime.now)
last_updated_by = models.ForeignKey(User, on_delete=models.DO_NOTHING, related_name="Last_Updated_By", verbose_name="Last Updated By")
def __str__(self):
return self.company_name
Any help appreciated!

You might have mistaken on how default value works in this case.
Your updated_datefield is kind of working while creating the row for the first time because of the dynamic default value datetime.now(). But it won't be automatically updated automatically later on. For this use I'd advise you to use DateTimeField with auto_now_add and auto_now set to True respectfully:
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
https://docs.djangoproject.com/en/3.1/ref/models/fields/#datefield
This way you won't need to update those values manually.
Sidenote: Those values are False by default so you don't need to specify auto_now_add=False.
Sadly I don't see why updating the last_updated_by field is not working properly if the Organization editing is otherwise working as intended. Changing the modified_date to work and trying again could be a good test to see if the Organization instance at hand is actually updated properly.

Related

Django - edit various number of formset and each form has own related values

I'm lost in django formsets. I tried many variations and none works as I need. Maybe it is a little bit confusing what my models are :)
What do I want to do? I need to create a view that displays all AssessParameters related to KapTSI and my problem is editing fields [assessment_requirements, value, finding]. Maximum what is was able to solve by using formset was editing those fields but how to display only the assessment_requirements those are related to edited parameter and to all parameters? And the bonus if there is a way with using CBV?
Models.py
class AssessParameter(models.Model):
application = models.ForeignKey(Application, on_delete=models.CASCADE, blank=True, null=True)
parameter = models.ForeignKey(Parameter, on_delete=models.DO_NOTHING)
requirement = models.TextField(blank=True)
assessment_requirements = models.ManyToManyField(Requirement, related_name="assessments", blank=True)
value = models.TextField(blank=True, null=True)
finding = models.ForeignKey(Finding, on_delete=models.DO_NOTHING)
note = models.TextField(blank=True)
documents = models.CharField(max_length=1, blank=True)
class KapTsi(models.Model):
title = models.CharField(max_length=150)
number = models.CharField(max_length=20)
tsi = models.ManyToManyField(Standard, related_name="tsis")
def __str__(self):
return f"{self.number} | {self.title}"
class ParameterGroup(models.Model):
title = models.CharField(max_length=150)
kap_tsi = models.ForeignKey(KapTsi, models.DO_NOTHING)
def __str__(self):
return f"{self.kap_tsi} {self.title}"
class Parameter(models.Model):
parameter_group = models.ForeignKey(ParameterGroup, on_delete=models.DO_NOTHING)
title = models.CharField(max_length=255)
standards = models.ManyToManyField(Standard, through="Specification", blank=True)
description = models.TextField(blank=True)
active = models.BooleanField(default=True)
def __str__(self):
return self.title
foms.py
class AssessParameterForm(forms.ModelForm):
class Meta:
model = AssessParameter
exclude = ['parameter', 'requirement', 'application']
AssessmentParameterFormSet = modelformset_factory(AssessParameter, form=AssessParameterForm, extra=0)
last try: views.py
def assessment_group(request, pk, slug, group):
application = Application.objects.get(id=pk)
group = ParameterGroup.objects.get(id=group)
assessments = AssessParameter.objects.filter(application=application).filter(parameter__parameter_group=group)
parameter = Requirement.objects.filter(parameter__parameter_group=group)
formset = AssessmentParameterFormSet(instance=assessments)
# for form in formset:
# form.fields['assessment_requirements'].queryset = parameter
context = {
'application': application,
'formset': formset,
}
return render(request, 'assessment/assessment-group.html', context)

Django submitting two forms cannot assign instance error

I have the following models/forms/view in which I have managed to submit to two different models as follows:
Models
class Account(models.Model):
username = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=150)
actflag = models.CharField(max_length=1, blank=True)
acttime = models.DateTimeField(blank=True, null=True)
comments = models.TextField(_('comments'), max_length=500, blank=True)
def __str__(self):
return self.name
class ISIN(models.Model):
code = models.CharField(max_length=12)
account_name = models.ForeignKey(Account, on_delete=models.SET_NULL, null=True)
actflag = models.CharField(max_length=1, blank=True)
acttime = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.code
Forms
from apps.portfolio.models import Account, ISIN
class PortfolioForm(forms.ModelForm):
class Meta:
model = Account
fields = ['name', 'comments']
class IdentifierForm(forms.ModelForm):
class Meta:
model = ISIN
fields = ['code']
View
def portfolios(request):
if request.user.is_authenticated:
if request.POST:
fm = PortfolioForm(request.POST)
fm2 = IdentifierForm(request.POST)
if fm.is_valid():
messages.success(request, 'Portfolio has been created.')
account = fm.save(commit=False)
account.username = request.user
account.acttime = timezone.now()
account.actflag = 'I'
account.save()
isin = fm2.save(commit=False)
#isin.account_name = account.name
isin.acttime = timezone.now()
isin.actflag = 'I'
isin.save()
return redirect('portfolios')
else:
fm = PortfolioForm()
fm2 = IdentifierForm()
context = {"name": request.user, "form": fm, "form2": fm2}
return render(request, 'portfolios.html', context)
else:
return redirect('login')
However, you will notice the commented line in my view: isin.account_name = account.name, when I uncomment this line and try to submit the forms again I get the following error: Cannot assign "'test'": "ISIN.account_name" must be a "Account" instance.
I believe it's to do with ForeignKey but still unsure how to store the newly created account name the user submitted within the isin model.
Help is much appreciated.
Although my answer solves the problem you originally had, there are a couple additional points that I wanted to make.
Improve naming and fix the original error
Your field is called account_name, and it implies that a string will be stored there. If it was actually a string, you would be able to do what you tried:
isin.account_name = account.name
In reality, you have a ForeignKey to the Account model, so you have to actually save a reference to the account object:
isin.account_name = account
It's a really good idea to have a foreign key instead of just a string because it avoids denormalization.
The problem here is the name of the field, account_name. If you later want to access the account name, you would have to write something like isis.account_name.name. Sounds wrong, doesn't it?
You could solve this by renaming your field like so:
class ISIN(models.Model):
code = models.CharField(max_length=12)
account = models.ForeignKey(Account, on_delete=models.SET_NULL, null=True)
actflag = models.CharField(max_length=1, blank=True)
acttime = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.code
Then, in your view, you would just isin.account = account, and later, if you wanted to access the name, you would use isin.account.name.
Another minor thing is that in some places an account is called Account and in other places it's Portfolio. This creates an illusion that they're unrelated entities and makes your code harder to read and maintain.
You probably should decide which one is the better term, and make it consistent everywhere.
Use builtin timestamp mechanism
Looks like you're using the acttime field to manually store creation time of accounts and ISINs.
You could use Django's auto_now_add property to do that automatically, like so:
class Account(models.Model):
acttime = models.DateTimeField(auto_now_add=True)
If you also wanted to store the last time an Account was updated, you could use auto_now (also renamed fields here for clarity):
class Account(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
And to stay DRY, you could make a mixin for that and use it in both Account and ISIN:
class TimeStampMixin(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Account(TimeStampMixin, models.Model):
username = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=150)
actflag = models.CharField(max_length=1, blank=True)
comments = models.TextField(_('comments'), max_length=500, blank=True)
def __str__(self):
return self.name
class ISIN(TimeStampMixin, models.Model):
code = models.CharField(max_length=12)
account = models.ForeignKey(Account, on_delete=models.SET_NULL, null=True)
actflag = models.CharField(max_length=1, blank=True)
def __str__(self):
return self.code
This way, the creation time and the latest update time are automatically stored in your models (the ones that inherit from TimeStampMixin).
Validate both forms
Looks like you're only checking one of the forms for validity, and not the other:
if fm.is_valid():
You should probably check both, in case ISIN.code is invalid:
if fm.is_valid() and fm2.is_valid():
What it means is that you have to make an instance of the account model by getting the name in order to save the form like so:
def portfolios(request):
if request.user.is_authenticated:
if request.POST:
fm = PortfolioForm(request.POST)
fm2 = IdentifierForm(request.POST)
if fm.is_valid():
messages.success(request, 'Portfolio has been created.')
account = fm.save(commit=False)
account.username = request.user
account.acttime = timezone.now()
account.actflag = 'I'
account.save()
# Here is where we get the instance of account
account = Account.objects.get(name=account.name)
isin = fm2.save(commit=False)
isin.account_name = account
isin.acttime = timezone.now()
isin.actflag = 'I'
isin.save()
return redirect('portfolios')
else:
fm = PortfolioForm()
fm2 = IdentifierForm()
context = {"name": request.user, "form": fm, "form2": fm2}
return render(request, 'portfolios.html', context)
else:
return redirect('login')
The field account_name is a ForeignKey to Account, but you are assigning an string. You should to assign an Account.
Change:
isin.account_name = account.name
To:
isin.account_name = account

Django save() always create a new object

I have this model in my app which is meant to auto-generate it's primary Key based on a method added in the save().
However, for each object, I will be expected to make updates of certain fields. Right now, anytime I make an update on the admin side (testing use cases) it instead creates a new record of the PK instead of updating the existing one. Any thoughts on how to remedy this?
class DeploymentTask(models.Model):
deployment_id = models.CharField(
'Deployment Task ID', primary_key=True, max_length=25, editable=False)
title = models.CharField(max_length=100)
current_status = FSMField('Current Status',
default=STATES[0], choices=STATES)
site_id = models.ForeignKey(
Site, related_name='+', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
refuel_record = models.ManyToManyField(RefuelRecord)
def __str__(self):
"""String for representing the Model object."""
return self.deployment_id
class Meta:
db_table = 'rm_deployment_task'
verbose_name_plural = 'Deployment Tasks'
def get_absolute_url(self):
return reverse('deployment_id-view', args=[str(self.deployment_id)])
def save(self):
today = datetime.datetime.now()
ticket_count = DeploymentTask.objects.filter(
created_at__year=today.year, created_at__month=today.month).count() + 1
new_task_id = 'DPT-' + str(str(datetime.date.today().year)) + str(
datetime.date.today().month).zfill(2) + str(
datetime.date.today().day).zfill(2) + '-' + str(ticket_count).zfill(6)
self.deployment_id = new_task_id
super(DeploymentTask, self).save()
enter image description here
You are always setting self.deployment_id to a new value in the save method.
Django tries to do UPDATE ... WHERE deployment_id = %, but there are no records with this id yet (at least if you are saving "old" object on different date or having different ticket_count)
If you want to update fields other than deployment_id, then simply do not set self.deployment_id if it's already set. If you want to update deployment_id then there is not straightforward way to do this, because it's used as primary key (but you can remember old pk and delete that object after you have created a new one during save)
Read more in the Django docs.
This is my updated code, which worked for me...
def make_id():
today = datetime.datetime.now()
ticket_count = DeploymentTask.objects.filter(
created_at__year=today.year, created_at__month=today.month).count() + 1
new_task_id = 'DPT-' + str(str(datetime.date.today().year)) + str(
datetime.date.today().month).zfill(2) + str(
datetime.date.today().day).zfill(2) + '-' + str(ticket_count).zfill(6)
return new_task_id
class DeploymentTask(models.Model):
deployment_id = models.CharField(
'Deployment Task ID', primary_key=True, max_length=25, editable=False, default=make_id)
title = models.CharField(max_length=100)
current_status = FSMField('Current Status',
default=STATES[0], choices=STATES)
site_id = models.ForeignKey(
Site, related_name='+', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
refuel_record = models.ManyToManyField(RefuelRecord)
def __str__(self):
"""String for representing the Model object."""
return self.deployment_id
class Meta:
db_table = 'rm_deployment_task'
verbose_name_plural = 'Deployment Tasks'
def get_absolute_url(self):
return reverse('deployment_id-view', args=[str(self.deployment_id)])

how we use fiter in view.py for multiilngual model in hvad

I am use multilingual for application i developed course application use hvad for that
models.py
from hvad.models import TranslatableModel, TranslatedFields, TranslationManager
class Course(TranslatableModel):
translations = TranslatedFields(
domain = models.ForeignKey('domain.Domain'),
#track = models.ForeignKey('track.Track'),
track = models.ForeignKey('track.Track', blank=True, null=True),
course_nm = models.CharField(max_length=100, verbose_name =_('Course Name')),
nature_of_training = models.TextField(verbose_name = _('Nature of Training')),
duration = models.PositiveIntegerField(verbose_name =_('Duration')),
sem = models.PositiveIntegerField(verbose_name =_('Semester')),
degree_level = models.CharField(max_length=100, verbose_name =_('Degree Level')),
credit = models.PositiveIntegerField(verbose_name =_('Credits')),
locations = models.CharField(max_length=100, verbose_name =_('Locations')),
accessible = models.CharField(max_length=100, verbose_name =_('Accessible')),
des = models.TextField(verbose_name = _('Description')),
admission_details = models.TextField(verbose_name = _('Admission Details')),
further_study_details = models.TextField(verbose_name = _('Further Study Details')),
seats = models.PositiveIntegerField(verbose_name =_('Seats')),
title = models.CharField(max_length=512, verbose_name=_('Title')),
slug = models.SlugField(max_length=512, verbose_name=_('Slug')),
created_date = models.DateTimeField(auto_now_add=True, blank=True, null=True),
updated_date = models.DateTimeField(auto_now=True, blank=True, null=True),
created_by = models.ForeignKey(User, blank=True, null=True, editable=False),
)
IN views.py
def get_context_data(self, **kwargs):
context = super(DegreeDetailView, self).get_context_data(**kwargs)
context['courses'] = Course.objects.all().order_by('track')
return context
when i use order_by in views.py so it give following error
To access translated fields like 'track' from an untranslated model, you must use a translation aware manager, you can get one using nani.utils.get_translation_aware_manager.
I search a lot but can't find solution, pls help!!
Thanks in Advance!!
It is too late since you asked this question.. but if there is anyone who suffers this kind of problem like me.
I used queryset like this:
Course.objects.untranslated().prefetch_related('translations').all().filter(translations__track__icontains='lorem ipsum')
And I recommend not putting ForeignKeys in translations( )

Django duplicate existing record in database

I'm trying to duplicate an existing record in a PostgreSQL DB, it seems to be duplicating by increments of 2 each time I hit the duplicate button. If there's 1 record in the database, once the button is hit it will create records 2 and 3.
Model
class Detail(models.Model):
created = models.DateTimeField(auto_now_add=True, blank=False)
last_update = models.DateTimeField(auto_now=True)
user = models.ForeignKey(User, related_name='+')
draft = models.BooleanField()
outage_name = models.ForeignKey(Outage, related_name='+')
group_name = models.CharField(max_length=100)
shift = models.CharField(max_length=6)
activity = models.CharField(max_length=100, null = False)
culture_title = models.ForeignKey(Culture, related_name='+')
work_completed = models.TextField()
work_planned = models.TextField()
radiation_info = models.TextField()
action_item = models.TextField()
lesson_learned = models.TextField()
View
def turnover_copy(request, id):
obj = Detail.objects.get(pk=id)
obj.pk = None
obj.draft = True
if obj.draft:
user = request.user.id
obj.user_id = user
obj.work_planned = 'My Work Planned.'
obj.save()
return HttpResponse('Created')
else:
return HttpResponse('Unable to duplicate template.')
EDIT: I had the def inside a for loop in the template, so it kept creating duplicates!
Are you sure the code isn't called twice for some reason? Some print statements might help you assert that.