Update another model's object after a form submit - django

I am attempting to update another model's object after a form submit. I would like the single object, already in existence within the Savings model, updated to reflect the most recent Entry submission.
Process:
Once the Entry form is filled out and submitted, I need the Savings object, which contains the "total" fields, to be updated.
The Savings fields take data from the Entry form, sums it, and posts it to its corresponding fields. For example, Savings.total_spent_euros sums Entry.euros_sums, and Savings.total_spent_dollars sums Entry.dollars_sum. That logic is not the issue. The problem I have is I currently have to open the Savings object in admin and save it every time I want the object to include the most recent entries. I would like to automate that.
models.py
class Entry(models.Model):
date = models.DateField(blank=True, null=True,)
euros = models.CharField(max_length=500, blank=True, null=True)
comments = models.CharField(max_length=900, blank=True, null=True)
euros_sum = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
xrate = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
dollars_sum = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
daily_savings_dollars = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
daily_savings_display = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
def get_absolute_url(self):
return reverse('argent:detail', kwargs={'pk': self.pk})
class Savings(models.Model):
total_spent_euros = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
total_spent_dollars = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
total_savings = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
total_savings_display = models.DecimalField(max_digits=6, decimal_places=2, blank=True, null=True)
Am I assuming correctly that my command must be entered within these two views? (I could be wrong):
views.py
class EntryCreate(CreateView):
form_class = EntryForm
template_name = 'argent/entry_form.html'
def form_valid(self, form):
Savings.objects.update(id=1)
return super(EntryCreate, self).form_valid(form)
class EntryUpdate(UpdateView):
model = Entry
form_class = EntryForm
template_name = 'argent/entry_form.html'
def form_valid(self, form):
Savings.objects.update(id=1)
return super(EntryUpdate, self).form_valid(form)
However, whenever I use Savings.objects.update(id=1) or Savings.objects.filter(id=1).update(), nothing happens. What am I overlooking here?
Thanks in advance for your help!
EDIT: Added a bit more detail.

SOLUTION
With the help of nik-m we finally came up with a solution. Here is my updated views.py. Not only did I have to use the .update solution, but I had to include all of the logic that is otherwise housed within my forms.py.
views.py
class EntryCreate(CreateView):
form_class = EntryForm
template_name = 'argent/entry_form.html'
def form_valid(self, form):
if form.save(self):
# total_euros_spent
sum_euros = Entry.objects.aggregate(s=Sum('euros_sum')).get('s')
sum_euros_f = "{0:.2f}".format(sum_euros)
# total_dollars_spent
sum_dollars = Entry.objects.aggregate(s=Sum('dollars_sum')).get('s')
sum_dollars_f = "{0:.2f}".format(sum_dollars)
# total_sum
sum_savings = Entry.objects.aggregate(s=Sum('daily_savings_dollars')).get('s')
sum_format = "{0:.2f}".format(sum_savings)
# total_sum_format
sum_abs_savings = Entry.objects.aggregate(s=Sum('daily_savings_dollars')).get('s')
absolute = abs(sum_abs_savings)
sum_abs = "{0:.2f}".format(absolute)
Savings.objects.filter(id=1).update(total_savings=sum_format, total_savings_display=sum_abs,
total_dollars_spent=sum_dollars_f, total_euros_spent=sum_euros_f)
return super(EntryCreate, self).form_valid(form)
else:
return self

Related

Django Admin, show inline based on slug

Have the following models
class FootballWebsite(models.Model):
"""Football service website."""
url = models.URLField, unique=True)
#football service
id = models.CharField(primary_key=True,
#is this domain blocked
blocked = models.BooleanField(default=False)
#is it online or offline
online = models.BooleanField(default=False)
updated = models.DateTimeField(auto_now=True, auto_now_add=True)
sub_categories = models.ForeignKey(SubCategory, default=1)
referral = models.TextField(blank=True)
mirror = models.ForeignKey('FootballWebsite', blank=True, null=True)
rank = models.PositiveIntegerField(default=0, blank=True, null=True)
screenshot = models.BooleanField(default=False)
class Meta:
"""Meta class."""
app_label = 'ahmia'
def __unicode__(self):
return u'%s' % (self.url)
"""The datetime when the football service was last seen online"""
try:
return self.footballwebsitestatus_set.filter(online=True).latest('time').time
except FootballWebsiteStatus.DoesNotExist:
return None
class FootballWebsiteDescription(models.Model):
"""Football service website description."""
about = models.ForeignKey(Footballebsite)
title = models.TextField(blank=True, null=True)
keywords = models.TextField(blank=True, null=True)
description = models.TextField(blank=True, null=True)
relation = models.URLField(blank=True, null=True)
subject = models.TextField(blank=True, null=True)
type = models.TextField(blank=True, null=True)
updated = models.DateTimeField(auto_now=True, auto_now_add=True)
language = models.TextField(null=True, blank=True)
contactInformation = models.TextField(null=True, blank=True)
officialInfo = models.BooleanField(default=False)
slug = AutoSlugField(populate_from=['title'], allow_duplicates=True, null=True)
class Meta:
"""Meta class."""
app_label = 'ahmia'
def __unicode__(self):
return unicode(self.about.url)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(FootballebsiteDescription, self).save(*args, **kwargs)
def __unicode__(self):
return u'%s' % (self.title)
I have a huge amount of links, and i would like to bulk assign them into a category or mark them as blocked based on identical title slug.
Managed to at least get a list of title_slugs with the code below, but the following step, i would like to get an inline list with all sites that have an identical title_slug and bulk assign those all in their a category
class FootballWebsiteInline(admin.TabularInline):
model = FootballWebsite
class FootballWebsiteDescriptionAdmin(admin.ModelAdmin):
list_display = ['show_slug']
def show_slug(self, obj):
return format_html("<a href='{url}'>{url}</a>", url=obj.slug)
inlines = [
FootballWebsiteInline,
]
Above code obviously doesn' t work, since the title slug which can appear many times is not a primary key.
Is it possible to get an inline list based on the title slug in this case which is not a primary key at all, or am i going about this the wrong way?
When possible at all, some further tweaking would be to group the identical title slugs

Django class based view, save in another model after CreateView

I have a create view (Loan_assetCreateView(generic.CreateView)) where I save if an asset is going to be loaned and when it will be returened in a model called Loan_asset(models.Model). Then I have the asset in a diffrent model Asset(model.Model). I would like to once I have saved my data in my Loan_assetCreateView(generic.CreateView) that is set the value in Asset.is_loaned to True. How can I do that?
My models.py:
class Asset(models.Model):
# Relationships
room = models.ForeignKey("asset_app.Room", on_delete=models.SET_NULL, blank=True, null=True)
model_hardware = models.ForeignKey("asset_app.Model_hardware", on_delete=models.SET_NULL, blank=True, null=True)
# Fields
name = models.CharField(max_length=30)
serial = models.CharField(max_length=30, unique=True, blank=True, null=True, default=None)
mac_address = models.CharField(max_length=30, null=True, blank=True)
purchased_date = models.DateField(null=True, blank=True)
may_be_loaned = models.BooleanField(default=False, blank=True, null=True)
is_loaned = models.BooleanField(default=False, blank=True, null=True)
missing = models.BooleanField(default=False, blank=True, null=True)
notes = HTMLField(default="")
ip = models.CharField(max_length=90, null=True, blank=True)
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
class Loan_asset(models.Model):
# Relationships
asset = models.ForeignKey("asset_app.Asset", on_delete=models.SET_NULL, blank=True, null=True)
loaner_type = models.ForeignKey("asset_app.Loaner_type", on_delete=models.SET_NULL, blank=True, null=True)
location = models.ForeignKey("asset_app.Locations", on_delete=models.SET_NULL, blank=True, null=True)
# Fields
loaner_name = models.CharField(max_length=60)
loaner_address = models.TextField(max_length=100, null=True, blank=True)
loaner_telephone_number = models.CharField(max_length=30)
loaner_email = models.EmailField()
loaner_quicklink = models.URLField(null=True, blank=True)
loan_date = models.DateField()
return_date = models.DateField()
notes = HTMLField(default="")
returned = models.BooleanField(default=False, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
class Meta:
pass
def __str__(self):
return str(self.loaner_name)
def get_absolute_url(self):
return reverse("asset_app_loan_asset_detail", args=(self.pk,))
def get_update_url(self):
return reverse("asset_app_loan_asset_update", args=(self.pk,))
my urls.py
`path("asset_app/loan_asset/create/", views.Loan_assetCreateView.as_view(), name="asset_app_loan_asset_create")`,
my views.py
class Loan_assetCreateView(generic.CreateView):
model = models.Loan_asset
form_class = forms.Loan_assetForm
Here are some options:
override form_valid method that's being called in post method implementation, so that after form will be validated (model instance saved), you'll be able to set the flag through foreign key/by creating Asset instance:
...
def form_valid(self, form):
self.object = form.save()
if self.object.asset:
self.object.asset.is_loaned = True
else:
self.object.asset = Asset.objects.create(is_loaned=True)
return HttpResponseRedirect(self.get_success_url())
use Django signals:
#receiver(post_save, sender=Loan_asset)
def create_transaction(sender, instance, created, **kwargs):
if created:
Asset.objects.create(is_loaned=True)
You can override the post method in your Loan_assetCreateView.
class Loan_assetCreateView(generic.CreateView):
model = models.Loan_asset
form_class = forms.Loan_assetForm
def post(request, *args, **kwargs):
response = super().post(request, *args. **kwargs)
# Do your thing
return response

Problems trying to populate sub form question fields with answers

First time poster here. I have been working on my first project for the last few months. And have spent an absorbent amount of time trying to get this one piece to work. I am able to display my configuration which has a drop down for config type. Depending on the Type selected it will display a list of "Attributes" (Questions) and I would like to have my form so that I can pick a type of config and answer the questions that pertain to that config. The part I am getting stuck on is the line in my view attrib_obj = get_object_or_404(config_attribs, id=1). This will display the first answer correctly for evey config because I hard coded it to show the answer to 1 but it will then display the first answer to every question. I am struggling on how to make this variable to be the id for every question and not just the first one. Thank you for any help. Oh and since i am new to this i am not sure if i am saving my form correctly either. :) Visual of my Problem
My Model
class Configuration(models.Model):
name = models.CharField(max_length=100, blank=True, null=True)
config_type = models.ForeignKey('ConfigType', on_delete=models.PROTECT, null=True)
company = models.ForeignKey('companies.Company', on_delete=models.PROTECT, blank=True, null=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("configurations:configuration-update", kwargs={"id": self.id})
class ConfigType(models.Model):
name = models.CharField(max_length=50, blank=False)
inactive = models.BooleanField(default=False)
def __str__(self):
return self.name
class Attribute(models.Model):
config_type = models.ForeignKey('ConfigType', on_delete=models.PROTECT, null=True)
number = models.IntegerField()
attribute = models.CharField(max_length=100, blank=False)
input_type = models.ForeignKey('InputType', on_delete=models.PROTECT, null=True)
required = models.BooleanField(default=False)
#answer = models.ManyToManyField('Answer')
inactive = models.BooleanField(default=False)
def __str__(self):
return self.attribute
class InputType(models.Model):
name = models.CharField(max_length=100, blank=False)
def __str__(self):
return self.name
class Answer(models.Model):
configuration = models.ForeignKey('Configuration', on_delete=models.PROTECT)
attribute = models.ForeignKey('Attribute', on_delete=models.PROTECT, null=True)
text = models.CharField(max_length=100, blank=True, null=True)
checkbox = models.BooleanField(blank=True, null=True)
number = models.IntegerField(blank=True, null=True)
date = models.DateField(blank=True, null=True)
def __str__(self):
return self.configuration.name + " - Attribute #" + str(self.attribute.number)
My View
#login_required(login_url='login')
def configuration_update_view(request, id=id):
obj = get_object_or_404(Configuration, id=id)
config_attribs = obj.config_type.attribute_set.all()
attrib_obj = get_object_or_404(config_attribs, id=1)
config_answers = obj.answer_set.all()
answer_obj = get_object_or_404(config_answers, attribute=attrib_obj)
form = ConfigurationForm(request.POST or None, instance=obj)
attrib_form = ConfigAttribForm(request.POST or None, instance=answer_obj)
if form.is_valid():
form.save()
if attrib_form.is_valid():
attrib_form.save()
context = {
'form': form,
'attrib_form': attrib_form,
'config_answers': config_answers,
'config_attribs': config_attribs
}
return render(request, "configurations/configuration_detail.html", context)

Multiple HTML option selection not working

I am developing a task management system using the django framework where supervisors can log in and assign tasks to multiple users using Django many to many field. When I log in to the admin portal, I can select multiple users at the same time which saves to the database well. But when I use the front end template, I am able to select multiple users but the selected options never get saved in the database and instead the field will be blank when viewing from the database table.
Here is my Model:
from django.contrib.auth.models import User
class Task(models.Model):
task_title = models.CharField(max_length=30, blank=True, null=True)
unit = models.ForeignKey(Unit, blank=True, null=True)
audit_phase_choice = (
('Pre Engagement', 'Pre Engagement'),
('Understanding Entity', 'Understanding Entity'),
('Risk Assessment', 'Risk Assessment'),
('Performing Audit', 'Performing Audit'),
('Report', 'Report'),
)
audit_phase = models.CharField(max_length=30, blank=True, null=True, choices=audit_phase_choice)
assigned_by = models.CharField(max_length=30, blank=True, null=True)
assigned_to = models.ManyToManyField(User, blank=True)
date_assigned = models.DateTimeField(auto_now_add=False, auto_now=False, blank=True, null=True)
status = models.CharField(max_length=30, blank=True, null=True)
completed = models.BooleanField('Task Completed', default=False)
date_completed = models.DateTimeField(auto_now_add=False, auto_now=False, blank=True, null=True)
start_date = models.DateTimeField(auto_now_add=False, auto_now=False, blank=True, null=True)
due_date = models.DateField(auto_now_add=False, auto_now=False, blank=True, null=True)
comment = models.TextField('comments', max_length=3000, default='', blank=True, null=True)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False, blank=True)
def __unicode__(self):
return self.task_title
def get_absolute_url(self):
return reverse("taskmis:user_task_edit", kwargs={"id": self.id})
Here is the form.py content
class TaskForm(forms.ModelForm):
class Meta:
model = Task
fields = ['task_title',
'unit',
'assigned_to',
'start_date',
'due_date',
'comment']
Here is the view.py content:
def user_task_entry(request):
title = 'Assign Task'
form = TaskForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.assigned_by = request.user
instance.save()
return redirect('taskmis:user_task_list')
context = {
"title": title,
"form": form,
}
return render(request, "task_entry.html",context)
You need to call save_m2m() manually because you set the commit=False when you call the save method
Django Ref
To work around this problem, every time you save a form using
commit=False, Django adds a save_m2m() method to your ModelForm
subclass. After you’ve manually saved the instance produced by the
form, you can invoke save_m2m() to save the many-to-many form data.
For example:

Save the Logged in User to the database model on form submission with django 1.8

I have a model and I am trying to save the user to the models database when the user submits the form. I had a site that did this but now my editor says "Use of super on an old style class"
I am using django 1.8 and i get
IntegrityError at /auction/createview/ NOT NULL constraint failed:
auction_auction.user_id
which is the nicest error I have been able to get. with all the tinkering i have done
class AuctionCreateView(LoginRequiredMixin,CreateView):
model = Auction
action = "created"
form_class = AuctionForm
auction_form = AuctionForm(initial={'user':request.user})
class AuctionForm(forms.ModelForm):
class Meta:
model = Auction
fields = (
"user",
"item_name",
"reserve",
"start_date",
"end_date",
"description",
"tags",
)
class Auction(models.Model):
user = models.ForeignKey(User)
item_id = models.CharField(max_length=255, blank=True, null=True)
item_name = models.CharField(max_length=255, blank=True, null=True)
winner = models.ForeignKey(User, related_name='Auction_Winner', blank=True, null=True)
reserve = MoneyField(max_digits=10, decimal_places=2, default_currency='USD')
created = models.DateTimeField(editable=False, null=True)
slug = AutoSlugField(('slug'), max_length=128, unique=True, populate_from=('item_name',))
start_date = models.DateTimeField(verbose_name="Start date")
end_date = models.DateTimeField(verbose_name="End date")
active = models.BooleanField(default=False, verbose_name='Active')
total_bids = models.IntegerField(default=0, verbose_name='Total bids')
date_added = models.DateTimeField(auto_now_add=True, verbose_name='Date added')
last_modified = models.DateTimeField(auto_now=True, verbose_name='Last modified')
description = models.TextField(max_length=3000)
tags = tagging.fields.TagField()
# bid_set = models.IntegerField(default= 0, verbose_name = "Bid set")
starting_amount = MoneyField(max_digits=10, decimal_places=2, default_currency='USD')
def __unicode__(self):
return '%s selling %s' % (self.user, self.item_name)
def _get_increment(self):
""" add some logic to base incrementing amount on starting price """
def get_absolute_url(self):
return reverse('auction_detail',
kwargs={'slug': self.slug})
when i saw this post I thought i'd be able to figure it out. thanks  ★ ✩
You need insert user_id before form save.
AuctionForm - need update request.user value. Added this fields from form initial.
You have to include 'user' on the fields of the Auction form class to solve that error and just put an initial parameter on the form instance in the views.py like
auction_form = AuctionForm(initial={'user':request.user})
because request.user on the form_valid method will not work at all