Trying to add a form to a DetailView - python

I am writing a Detail View - and the form that is displayed is the particular Track that the user is adding/updating to the CD Entry (It's a CD Library application). The trick is I want to see everything else about the CD on the page as well as the form for the particular track. Having trouble figuring out how to get the form to be just the trac I am adding/updating.
Basically you can enter a CD Title,Artist, and total time of the CD into the CD model. Then the Tracks are stored in the Track Model, with their Foreign Key being the CD they are on.
While looking at a Detail View of the CD, the user can add or update a Particular track via a button. I'm trying to get the update part working.
Model Looks like this:
from django.db import models
from datetime import datetime, timedelta
class Cd(models.Model):
artist_name = models.CharField(max_length=155)
cd_title = models.CharField(max_length=155)
cd_total_time = models.TimeField(default="00:00:00")
cd_total_time_delta = models.DurationField(default=timedelta)
cd_run_time = models.TimeField(default="00:00:00",blank=True)
cd_run_time_delta = models.DurationField(default=timedelta)
cd_remaining_time = models.TimeField(default="00:00:00",blank=True)
cd_remaining_time_delta = models.DurationField(default=timedelta)
def save(self, *args, **kwargs):
super().save(*args,**kwargs)
def __str__(self):
return f"{self.artist_name} : {self.cd_title}"
class Track(models.Model):
cd_id = models.ForeignKey(Cd, on_delete=models.CASCADE,
related_name='cd_tracks',
)
track_title = models.CharField(max_length=50)
track_number = models.IntegerField()
trk_length_time = models.TimeField(null=True,default=None, blank=True)
trk_length_time_delta = models.DurationField(default=timedelta)
trk_run_time = models.TimeField(default="00:00:00",blank=True)
trk_run_time_delta = models.DurationField(default=timedelta)
trk_remaining_time = models.TimeField(default="00:00:00",blank=True)
trk_remaining_time_delta = models.DurationField(default=timedelta)
def save(self, *args, **kwargs):
super().save(*args,**kwargs)
self.cd_id.save()
def __str__(self):
return f"{self.track_title}"
The views.py snippet:
class Track_update(UpdateView):
model = Track
template_name = 'track_update.html'
fields = [ 'cd_id', 'track_title',
'track_number', 'trk_length_time'
]
success_url = "/"
class Cd_DetailView(DetailView):
model = Cd
template_name = 'cd_detail.html'
fields = ['artist_name','cd_title','cd_total_time',
'cd_run_time','cd_remaining_time'
]
class Cd_MixedView(FormMixin, DetailView):
model = Cd
template_name = 'cd_mixed_view.html'
form_class = TrackForm
def get_success_url(self):
return reverse('cd_detail {id}')
def get_context_data(self, **kwargs):
context = super(Cd_MixedView,self).get_context_data(**kwargs)
context['form'] = TrackForm(initial={
'cd_id':self.object
})
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self,form):
form.save()
return super(Cd_MixedView, self).form_valid(form)
The urls.py looks like
from django.urls import path
from django.contrib import admin
from .views import (
HomePageView,
Cd_Create,
Cd_Update,
Cd_DetailView,
List_Cds,
Track_Create,
Cd_MixedView,
)
urlpatterns = [
path('', HomePageView.as_view(), name = 'home'),
path('admin/', admin.site.urls),
path('cd/add/', Cd_Create.as_view(), name='cd_new'),
path('cd/update/<int:pk>', Cd_Update.as_view(), name='cd_update'),
path('cd/detail/<int:pk>', Cd_DetailView.as_view(), name='cd_detail'),
path('track/add/', Track_Create.as_view(), name='track_new'),
path('track/add/<int:pk>', Cd_MixedView.as_view(), name='cd_mixed_view'),
path('list/', List_Cds.as_view(), name='list_cds'),
]
The template looks like this:
<!-- templates/cd_mixed_view.html -->
{% extends 'base.html' %}
{% load static %}
{% block title %} CD Details{% endblock title %}
{% block content %}
<h1>CD Update Track </h1>
<p>Artist Name: {{ cd.artist_name}}
<p>Cd Title: {{ cd.cd_title }}
<p>Cd Total Time: {{ cd.cd_total_time|time:"H:i:s" }}
<p>Cd Run Time: {{ cd.cd_run_time|time:"H:i:s" }}
<p>Cd Remaining Time:
{% if cd.cd_run_time_delta > cd.cd_total_time_delta %}
(-{{ cd.cd_remaining_time|time:"H:i:s" }})
{% else %}
{{ cd.cd_remaining_time|time:"H:i:s" }}
{% endif %}
<TABLE BORDER="0" TABLE_LAYOUT="fixed" WIDTH="100%">
<TR BGCOLOR="#B0B0FF">
<TD></TD>
<TD ALIGN="Center"> Track #</TD>
<TD ALIGN="Center"> Cut Title</TD>
<TD ALIGN="Center">Track Length</TD>
<TD ALIGN="Center" BGCOLOR="#CC99CC">Run Time</TD>
<TD ALIGN="Center" BGCOLOR="#CC99CC">Time Remaining</TD>
</TR>
{% for tracks in cd.cd_tracks.all %}
*** This is the part I am having trouble with ***
{% if tracks.id = track number that was clicked %}
{form_as_p}
{% else %}
<TR>
<TD ALIGN="Center" rowspan="1" height="33" width="33">
<TD ALIGN="Left" VALIGN="Top" WIDTH="10"> {{ tracks.track_number }}</TD>
<TD ALIGN="Left" VALIGN="Top"> {{ tracks.track_title }}</TD>
<TD ALIGN="Left" VALIGN="Top">{{ tracks.trk_length_time|time:"H:i:s" }}</TD>
<TD ALIGN="Left" VALIGN="Top">{{ tracks.trk_run_time|time:"H:i:s" }}</TD>
{% if tracks.trk_run_time_delta > cd.cd_total_time_delta %}
<TD BGCOLOR="#8DF1BF" ALIGN="Left"> (-{{ tracks.trk_remaining_time|time:"H:i:s" }})</TD>
{% else %}
<TD BGCOLOR="#8DF1BF" ALIGN="Left"> {{ tracks.trk_remaining_time|time:"H:i:s" }}</TD>
{% endif %}
</TR>
{% endfor %}
</TABLE>
</table>
{% endblock content %}
So, how do I pass the Track ID into the template, so that I can display the form for THAT track, otherwise
display the track information for all the other tracks.
Any information about how to approach this would be greatly appreciated.
Below is a picture of what the Detail Screen looks like before attempting to update the Track.
Thanks

Did you think about adding the tracking id as a slug, creating another function in views, to pass it? with an 'a' tag, and passing an href, with inside it the foreign key if the track referring to the CD. You could pass the values in the context of the view.

Related

Attempting to set related model fields via parent function call(s)

I am writing a Compact Disk Library editing routine. The application allows you to enter a CD Artist Name, and CD Title and the total time of the CD. User can then enter track information. While entering track information I want the application to display Current run time including this track, and time remaining based on length of the CD. The models and functions are below
class Cd(models.Model):
artist_name = models.CharField(max_length=155)
cd_title = models.CharField(max_length=155)
cd_total_time = models.TimeField(default="00:00:00")
cd_total_time_delta = models.DurationField(default=timedelta)
cd_run_time = models.TimeField(default="00:00:00",blank=True)
cd_run_time_delta = models.DurationField(default=timedelta)
cd_remaining_time = models.TimeField(default="00:00:00",blank=True)
cd_remaining_time_delta = models.DurationField(default=timedelta)
def convert_to_delta(self,time_in):
hold_time = time_in.strftime("%H:%M:%S")
t = datetime.strptime(hold_time,"%H:%M:%S")
return(timedelta(hours=t.hour, minutes=t.minute, seconds=t.second))
def calculate_time(self):
cd_total_time_delta = self.convert_to_delta(self.cd_total_time)
cd_total_run_time_delta = timedelta(minutes=0)
for track in self.cd_tracks.all():
cd_total_run_time_delta += track.trk_length_time_delta
track.trk_run_time_delta += cd_total_run_time_delta
track.trk_run_time = f"{track.trk_run_time_delta}"
track.trk_remaining_time_delta = cd_total_time_delta - cd_total_run_time_delta
track.trk_remaining_time = f"{track.trk_remaining_time_delta}"
self.cd_run_time_delta = cd_total_run_time_delta
self.cd_run_time = f"{self.cd_run_time_delta}"
self.cd_remaining_time_delta = self.cd_total_time_delta - cd_total_run_time_delta
self.cd_remaining_time = f"{abs(self.cd_remaining_time_delta)}"
def save(self, *args, **kwargs):
self.calculate_time()
super().save(*args,**kwargs)
def __str__(self):
return f"{self.artist_name} : {self.cd_title}"
class Track(models.Model):
cd_id = models.ForeignKey(Cd, on_delete=models.CASCADE,
related_name='cd_tracks',
)
track_title = models.CharField(max_length=50)
track_number = models.IntegerField()
trk_length_time = models.TimeField(null=True,default=None, blank=True)
trk_length_time_delta = models.DurationField(default=timedelta)
trk_run_time = models.TimeField(default="00:00:00",blank=True)
trk_run_time_delta = models.DurationField(default=timedelta)
trk_remaining_time = models.TimeField(default="00:00:00",blank=True)
trk_remaining_time_delta = models.DurationField(default=timedelta)
def calculate_track_delta(self):
self.trk_length_time_delta = self.cd_id.convert_to_delta(self.trk_length_time)
def save(self, *args, **kwargs):
self.calculate_track_delta()
super().save(*args,**kwargs)
self.cd_id.save()
def __str__(self):
return f"{self.track_title}"
When I attempt to update a track by entering in the track length time, name etc. and pressing submit, it should calculate all of the track data for each track. It DOES calculate the CD Total run time and remaining time, but the track values do not get updated. I am thinking my code isn't actually referencing the actual object correctly? Or the parent is not saving the child data...(how would I write that?).
Thanks
This is what the actual Parent Update Form looks like:
The Run Time I entered manually via admin, to see if that helped (it didn't). The time remaining should be the total time minus run time (run time increases of course for each track).
below is the template itself:
{% extends 'base.html' %}
{% load static %}
{% block title %} Update CD {% endblock title %}
{% block content %}
<h1>CD Update </h1>
{% if submitted %}
<p>
Your CD information was submitted successfully. Thank You.
</p>
{% else %}
<form action="" method="post" novalidate>
<table>
{{ form.as_table }}
{% if not cd.cd_tracks.all %}
No Tracks Added
{% else %}
<TABLE BORDER="0" TABLE_LAYOUT="fixed" WIDTH="100%">
<TR BGCOLOR="#B0B0FF">
<TD></TD>
<TD ALIGN="Center"> Track #</TD>
<TD ALIGN="Center"> Cut Title</TD>
<TD ALIGN="Center">Track Length</TD>
<TD ALIGN="Center" BGCOLOR="#CC99CC">Run Time</TD>
<TD ALIGN="Center" BGCOLOR="#CC99CC">Time Remaining</TD>
</TR>
{% for tracks in cd.cd_tracks.all %}
<TR>
<TD ALIGN="Center" rowspan="1" height="33" width="33">
<!-- Turning Off anchor No mix view yet
<A href=" url 'cd_mixed_view' cd.pk tracks.pk ">
-->
<IMG SRC="{% static 'images/edit.gif' %}">
</A>
</TD>
<TD ALIGN="Left" VALIGN="Top" WIDTH="10"> {{ tracks.track_number }}</TD>
<TD ALIGN="Left" VALIGN="Top"> {{ tracks.track_title }}</TD>
<TD ALIGN="Left" VALIGN="Top">{{ tracks.trk_length_time|time:"H:i:s" }}</TD>
<TD ALIGN="Left" VALIGN="Top">{{ tracks.trk_run_time|time:"H:i:s" }}</TD>
{% if tracks.trk_remaining_time_delta < "00:00:00" %}
<TD BGCOLOR="#8DF1BF" ALIGN="Left"> (-{{ tracks.trk_remaining_time|time:"H:i:s" }})</TD>
{% else %}
<TD BGCOLOR="#8DF1BF" ALIGN="Left"> {{ tracks.trk_remaining_time|time:"H:i:s" }}</TD>
{% endif %}
</TR>
{% endfor %}
</TABLE>
{% endif %}
<tr>
<td><input type="submit" value="Submit"></td>
</tr>
</table>
{% csrf_token %}
</form>
{% endif %}
{% endblock content %}
The code was close. The most major problem was I was not saving the child model in the parent correctly (or at all really).
Uddate model is :
class Cd(models.Model):
artist_name = models.CharField(max_length=155)
cd_title = models.CharField(max_length=155)
cd_total_time = models.TimeField(default="00:00:00")
cd_total_time_delta = models.DurationField(default=timedelta)
cd_run_time = models.TimeField(default="00:00:00",blank=True)
cd_run_time_delta = models.DurationField(default=timedelta)
cd_remaining_time = models.TimeField(default="00:00:00",blank=True)
cd_remaining_time_delta = models.DurationField(default=timedelta)
def convert_to_delta(self,time_in):
hold_time = time_in.strftime("%H:%M:%S")
t = datetime.strptime(hold_time,"%H:%M:%S")
return(timedelta(hours=t.hour, minutes=t.minute, seconds=t.second))
def calculate_time(self):
cd_total_time_delta = self.convert_to_delta(self.cd_total_time)
cd_total_run_time_delta = timedelta(minutes=0)
for track in self.cd_tracks.all():
cd_total_run_time_delta += track.trk_length_time_delta
track.trk_run_time_delta = cd_total_run_time_delta
track.trk_run_time = f"{track.trk_run_time_delta}"
track.trk_remaining_time_delta = cd_total_time_delta - cd_total_run_time_delta
track.trk_remaining_time = f"{abs(track.trk_remaining_time_delta)}"
super(Track,track).save()
self.cd_run_time_delta = cd_total_run_time_delta
self.cd_run_time = f"{self.cd_run_time_delta}"
self.cd_remaining_time_delta = self.cd_total_time_delta - cd_total_run_time_delta
self.cd_remaining_time = f"{abs(self.cd_remaining_time_delta)}"
def save(self, *args, **kwargs):
self.calculate_time()
super().save(*args,**kwargs)

When trying to create a new Django CreateView form, my forms are being populated with the data that was entered in the last form

Using Class Based Views, ModelForms, and Inlline Formsets. I’m making a recipe application in Django. Each user has their own OneToOne RecipeBook object, which in turn can hold as many recipes as needed, as each Recipe has a ForeignKey relationship to the RecipeBook object. There are also Ingredient and Direction objects that each have a FK relationship to the Recipe object.
The good news is that I can create a Recipe object using my CreateView, with as many associated Ingredient and Direction objects as I want. The Ingredient/Direction objects should be unique to each Recipe object (and by extension, each User). However, when I create a Recipe object, and then I try to create a new Recipe object, its Ingredient and Direction fields are already populated on the new object, form the old object. So if I had just created a Recipe with 3 Ingredient/Direction fields all set to '1', and then go to create another Recipe, the new Recipe object will have all blank fields, but will have 3 Ingredient/Direction objects all set to 1. This will happen to each user that is logged in. I want to make it so these objects are all staying together.
I think the issue to this is that either my get_context_data or my form_valid methods are saving the Ingredient/Direction objects globally, when I just want each Ingredient/Direction object to only be associated with the specific recipe object. I’ve tried messing with the init function of my Forms, I’ve tried querying for the object before/while its being created, and it seems like no matter what I do I’m just running in circles. I’d appreciate any help/resources anyone can point me towards!
My Models:
class RecipeBook(models.Model):
"""Each user has a single associated RecipeBook object, linked in this OneToOne field"""
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
class Recipe(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4())
recipebook = models.ForeignKey(RecipeBook, related_name='recipe_set', on_delete=models.CASCADE)
title = models.CharField(max_length=150, help_text='Title of the recipe')
description = models.TextField(help_text='Description of the recipe', blank=True)
# image = models.ImageField(height_field=, width_field=, help_text='Image of the recipe', blank=True)
servings = models.PositiveSmallIntegerField(help_text='The amount of servings the recipe will yield', default=0, blank=True)
prep_time = models.PositiveSmallIntegerField(help_text='The preparation time', default=0, blank=True)
cook_time = models.PositiveSmallIntegerField(help_text='The cooking time', default=0, blank=True)
url = models.URLField(blank=True)
TIME_UNITS = (
('m', 'Minutes'),
('h', 'Hours')
)
def get_absolute_url(self):
return reverse('recipe_book:recipe-detail', args=[str(self.id)])
def __str__(self):
return self.title
class Ingredient(models.Model):
recipe = models.ForeignKey(Recipe, related_name='ingredient_set', on_delete=models.CASCADE)
name = models.CharField(max_length=100)
amount = models.CharField(max_length=20, blank=True)
def __str__(self):
return self.name
class Direction(models.Model):
recipe = models.ForeignKey(Recipe, related_name='direction_set', on_delete=models.CASCADE)
step_instructions = models.TextField(help_text='Write the instructions of the step here')
My Forms:
class AddRecipeForm(ModelForm):
recipe = forms.ModelChoiceField(queryset=Recipe.objects.all())
class Meta:
model = Recipe
fields = ['title', 'description', 'servings', 'prep_time', 'cook_time', 'url']
class AddIngredientForm(ModelForm):
class Meta:
model = Ingredient
fields = ['name', 'amount']
# def __init__(self, *args, **kwargs):
# self.recipe = kwargs.pop('recipe')
# super(AddIngredientForm, self).__init__(*args, **kwargs)
#
# if not self.instance:
# self.fields['name'].initial = self.recipe.default_name
# self.fields['amount'].widget = forms.TextInput(required=False)
#
# def save(self, *args, **kwargs):
# self.instance.recipe = self.recipe
# ingredient = super(AddIngredientForm, self).save(*args, **kwargs)
# return ingredient
IngredientFormset = inlineformset_factory(Recipe, Ingredient, form=AddIngredientForm, extra=1, can_delete=True)
class AddDirectionForm(ModelForm):
class Meta:
model = Direction
fields = ['step_instructions']
# def __init__(self, *args, **kwargs):
# self.recipe = kwargs.pop('recipe')
# super(AddDirectionForm, self).__init__(*args, **kwargs)
#
# if not self.instance:
# self.fields['step_instructions'].initial = self.recipe.default_step_instructions
#
# def save(self, *args, **kwargs):
# self.instance.recipe = self.recipe
# direction = super(AddDirectionForm, self).save(*args, **kwargs)
# return direction
DirectionFormset = inlineformset_factory(Recipe, Direction, form=AddDirectionForm, extra=1, can_delete=True)
My View:
class RecipeListView(LoginRequiredMixin, generic.ListView):
model = models.Recipe
context_object_name = 'recipes'
# Using this method ensures that the only recipes that are displayed are the ones associated with each user
def get_queryset(self):
return models.Recipe.objects.filter(recipebook=self.request.user.recipebook)
class RecipeDetailView(LoginRequiredMixin, generic.DetailView):
model = models.Recipe
fields = ['title', 'description', 'servings', 'prep_time', 'cook_time', 'url']
context_object_name = 'recipe'
def get_queryset(self):
return models.Recipe.objects.filter(recipebook=self.request.user.recipebook)
# Classes used to actually create full recipe objects
class RecipeCreate(LoginRequiredMixin, CreateView):
model = models.Recipe
fields = ['title', 'description', 'servings', 'prep_time', 'cook_time', 'url']
def get_queryset(self):
return models.Recipe.objects.filter(recipebook=self.request.user.recipebook)
def get_context_data(self, **kwargs):
data = super(RecipeCreate, self).get_context_data(**kwargs)
#user = self.request.user
if self.request.POST:
data['ingredients'] = IngredientFormset(self.request.POST)
#queryset=models.Recipe.objects.filter(recipebook=self.request.user.recipebook))
data['directions'] = DirectionFormset(self.request.POST)
#queryset=models.Recipe.objects.filter(recipebook=self.request.user.recipebook))
else:
data['ingredients'] = IngredientFormset()
#queryset=models.Recipe.objects.filter(self.kwargs['id']))
data['directions'] = DirectionFormset()
#queryset=models.Recipe.objects.filter(self.kwargs['id']))
return data
def form_valid(self, form):
form.instance.recipebook = self.request.user.recipebook
context = self.get_context_data()
ingredients = context['ingredients']
directions = context['directions']
# self.object is the object being created
self.object = form.save()
if ingredients.is_valid():
ingredients.instance = self.object
ingredients.save()
if directions.is_valid():
directions.instance = self.object
directions.save()
return super(RecipeCreate, self).form_valid(form)
class RecipeUpdate(LoginRequiredMixin, UpdateView):
model = models.Recipe
fields = ['title', 'description', 'servings', 'prep_time', 'cook_time', 'url']
def get_context_data(self, **kwargs):
data = super(RecipeUpdate, self).get_context_data(**kwargs)
if self.request.POST:
data['ingredients'] = IngredientFormset(self.request.POST, instance=self.object)
#queryset=models.Recipe.objects.filter(recipebook=self.request.user.recipebook))
data['directions'] = DirectionFormset(self.request.POST, instance=self.object)
#queryset=models.Recipe.objects.filter(recipebook=self.request.user.recipebook))
else:
data['ingredients'] = IngredientFormset(instance=self.object)
#queryset=models.Recipe.objects.filter(recipebook=self.request.user.recipebook))
data['directions'] = DirectionFormset(instance=self.object)
#queryset=models.Recipe.objects.filter(recipebook=self.request.user.recipebook))
return data
def form_valid(self, form):
form.instance.recipebook = self.request.user.recipebook
context = self.get_context_data()
ingredients = context['ingredients']
directions = context['directions']
self.object = form.save()
if ingredients.is_valid():
ingredients.instance = self.object
ingredients.save()
if directions.is_valid():
directions.instance = self.object
directions.save()
return super(RecipeUpdate, self).form_valid(form)
My Template:
{% extends 'base-recipe.html' %}
{# https://simpleit.rocks/python/django/dynamic-add-form-with-add-button-in-django-modelformset-template/ #}
{% block content %}
<div class="container">
<div class="card">
<div class="card-header">Create Recipe</div>
<div class="card-body">
<form action="" method="POST"> {% csrf_token %}
{# table for the Recipe object, manually rendering it for more control #}
<table class="table">
<tr>
<td>{{ form.title.label_tag }}</td>
<td>{{ form.title }}</td>
</tr>
<tr>
<td>{{ form.description.label_tag }}</td>
<td>{{ form.description }}</td>
</tr>
<tr>
<td>{{ form.servings.label_tag }}</td>
<td>{{ form.servings }}</td>
</tr>
<tr>
<td>{{ form.prep_time.label_tag }}</td>
<td>{{ form.prep_time }}</td>
</tr>
<tr>
<td>{{ form.cook_time.label_tag }}</td>
<td>{{ form.cook_time }}</td>
</tr>
<tr>
<td>{{ form.url.label_tag }}</td>
<td>{{ form.url }}</td>
</tr>
</table>
{# table for the ingredient(s) object(s) #}
<table class="table">
{{ ingredients.management_form }}
{% for form in ingredients.forms %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<tr class="{% cycle row1 row2 %} formset_row-{{ ingredients.prefix }}">
{% for field in form.visible_fields %}
<td>
{# include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<table class="table">
{{ directions.management_form }}
{% for form in directions.forms %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<!--<tr class="{% cycle row1 row2 %} formset_row-{{ directions.prefix }}">-->
<tr class="formset_row-{{ directions.prefix }}">
{% for field in form.visible_fields %}
<td>
{# include the hidden fields #}
{% if forloop.first %}
{% for field in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input class="btn btn-primary" type="submit" value="Submit">
<a class="btn btn-danger" href="{% url 'recipe_book:index' %}">Back to the recipe list</a>
</form>
</div>
</div>
</div>
{% load static %}
<script src="{% static 'js/jquery.formsets.js' %}"></script>
<script type="text/javascript">
$('.formset_row-{{ ingredients.prefix }}').formset({
addText: 'Add Another Ingredient',
deleteText: 'Remove',
prefix: '{{ ingredients.prefix }}',
});
$('.formset_row-{{ directions.prefix }}').formset({
addText: 'Add another',
deleteText: 'Remove',
prefix: '{{ directions.prefix }}',
});
</script>
{% endblock %}
For anyone who also has this issue, here's the fix, from the Django forum's user KenWhitesell:
You can chase this down through the source code if you really want to
understand what’s going on, but the Reader’s Digest version is that an
inline formset is created under the assumption that the formset is
linked to an existing instance. If one isn’t supplied, it selects one
from the database.
The fix, for me, was in the CreateView's get_context_data() method: since we don't want the inline_formset to be querying for any objects on a CreateView, you have to explicitly tell it not to with a queryset parameter on the GET request like this:
def get_context_data(self, **kwargs):
data = super(RecipeCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['ingredients'] = IngredientFormset(self.request.POST)
data['directions'] = DirectionFormset(self.request.POST)
else:
data['ingredients'] = IngredientFormset(queryset=models.Ingredient.objects.none())
data['directions'] = DirectionFormset(queryset=models.Direction.objects.none())
return data
Here's a link to our forum post discussing the issue: https://forum.djangoproject.com/t/my-forms-are-being-populated-with-the-data-that-was-entered-in-the-last-createview/6278/12

how to sum elements of a model and return the total in a django template?

I'm new to Django and after spending a lot of time on this (and still being stuck) I thought to ask for some expert help!
In this small project, what I'm trying to achieve is a webpage that returns the total amount in GBP of a user's investments (only cash investments for now). I have created some python scripts to get the FX rates.
I'm trying to sum up all the amounts in GBP however I'm stuck! I don't know whether I should do this in the model (Cash) or in the view and how to then show it in the template(cash_list.html).
Your help would be really appreciated.
Workings below
models.py
from django.db import models
class fx_table(models.Model):
eur_to_gbp_pair = 'EUR/GBP'
aud_to_gbp_pair = 'AUD/GBP'
usd_to_gbp_pair = 'AUD/USD'
eur_to_gbp = models.FloatField(default=0)
aud_to_gbp = models.FloatField(default=0)
usd_to_gbp = models.FloatField(default=0)
date_time = models.CharField(max_length=264,unique=True,default='')
class Cash(models.Model):
reference = models.CharField(max_length=128)
amount = models.FloatField(default=0)
currency = models.CharField(max_length=128)
forms.py
from django import forms
from fx_rates_app.models import Cash
from fx_rates_app.models import Shares
class NewUserFormCash(forms.ModelForm):
class Meta():
model = Cash
fields = '__all__' #You want to be able to change all the fields
class NewUserFormShares(forms.ModelForm):
class Meta():
model = Shares
fields = '__all__'
Python script
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE','fx_rates_project.settings')
import django
django.setup()
from fx_rates_app.models import fx_table
import pandas_datareader.data as web
from datetime import datetime
os.environ["ALPHAVANTAGE_API_KEY"] = 'XXXXXXXXXX'
fx_gbp_to_eur = web.DataReader("GBP/EUR","av-forex")
eur = float(fx_gbp_to_eur[4:5].values[0][0])
fx_gbp_to_aud = web.DataReader("GBP/AUD","av-forex")
aud = float(fx_gbp_to_aud[4:5].values[0][0])
fx_gbp_to_usd = web.DataReader("GBP/USD","av-forex")
usd = float(fx_gbp_to_usd[4:5].values[0][0])
from datetime import datetime
now = datetime.now()
dt_string = now.strftime("%d/%m/%Y %H:%M:%S")
webpg1 = fx_table.objects.get_or_create(eur_to_gbp=eur,aud_to_gbp=aud,usd_to_gbp=usd,date_time=dt_string)[0]
views.py
from django.shortcuts import render
from fx_rates_app.models import fx_table
from fx_rates_app.forms import NewUserFormCash,NewUserFormShares
from django.views.generic import TemplateView,ListView
from django.urls import reverse
from fx_rates_app import forms
from fx_rates_app.models import Cash, Shares,fx_table
import datetime
class CashListView(ListView):
model = Cash
context_object_name = 'cash_details'
def get_context_data(self, **kwargs):
context = super(CashListView, self).get_context_data(**kwargs)
context.update({
'fx_list': fx_table.objects.all(),
'insert': datetime.datetime.now(),
})
return context
class SharesListView(ListView):
model = Shares
context_object_name = 'shares_details'
def get_context_data(self, **kwargs):
context = super(SharesListView, self).get_context_data(**kwargs)
context.update({
'fx_list': fx_table.objects.all(),
})
return context
def Cash(request):
form_cash = NewUserFormCash()
if request.method == "POST":
form_cash = NewUserFormCash(request.POST)
if form_cash.is_valid():
form_cash.save(commit=True)
return index(request)
else:
print('ERROR FORM INVALID')
return render(request,'fx_rates_app/cash_positions.html',{'form':form_cash})
urls.py
from django.urls import path
from fx_rates_app import views
from fx_rates_app.views import CashListView,SharesListView,FxListView
app_name = 'fx_rates_app'
urlpatterns = [
path('fx/',FxListView.as_view(), name='fx'),
path('cash_positions/',views.Cash,name='cash_positions'),
path('shares/',views.Shares,name='shares'),
path('cash_list/',CashListView.as_view(), name='cash_list'),
path('shares_list/',SharesListView.as_view(), name='shares_list'),
]
cash_list.html
{% extends 'fx_rates_app/base.html' %}
{% block body_block %}
{% load mathfilters %}
<div class="container">
<table class="table table-bordered">
<thead>
<th>Reference</th>
<th>Amount</th>
<th>Currency</th>
<th>Equivalent in GBP</th>
</thead>
{% for item in cash_details %}
{% for ele in fx_list %}
<tr>
<td> {{ item.reference }} </td>
<td> {{ item.amount|floatformat:0|intcomma }} </td>
<td> {{ item.currency }} </td>
{% if item.currency == "AUD" %}
<td>{{ item.amount|div:ele.aud_to_gbp|floatformat:0|intcomma }}</td>
{% elif item.currency == "EUR" %}
<td>{{ item.amount|div:ele.eur_to_gbp|floatformat:0|intcomma }}</td>
{% elif item.currency == "USD" %}
<td>{{ item.amount|div:ele.usd_to_gbp|floatformat:0|intcomma }}</td>
{% else %}
<td> {{ item.amount|floatformat:0|intcomma }} </td>
{% endif %}
</tr>
{% endfor %}
{% endfor %}
<tr>
<td> Total </td>
<td> </td>
<td> </td>
<td> Total in GBP here</td>
</tr>
{% endblock %}
</div>
Thanks in advance,
Alex
I think you can try like this in view:
from django.db.models import Sum, When, Case, FloatField, Value, F
class CashListView(ListView):
model = Cash
context_object_name = 'cash_details'
def get_context_data(self, **kwargs):
context = super(CashListView, self).get_context_data(**kwargs)
latest_fx = fx_table.objects.last()
queryset = context['cash_details'].annotate(amount_in_gbp=Case(
When(currency="AUD", then=F('amount')/latest_fx.aud_to_gbp),
When(currency="USD", then=F('amount')/latest_fx.usd_to_gbp),
When(currency="EUR", then=F('amount')/latest_fx.eur_to_gbp),
default=F('amount'),
output_field=FloatField()
)
)
context['cash_details'] = queryset
context['total_in_gbp'] = queryset.aggregate(sum=Sum('amount_in_gbp'))['sum']
return context
# template
{% for item in cash_details %}
<tr>
<td> {{ item.reference }} </td>
<td> {{ item.amount|floatformat:0|intcomma }} </td>
<td> {{ item.currency }} </td>
<td> {{ item.amount_in_gbp|floatformat:0|intcomma }} </td>
</tr>
{% endfor %}
Total: {{ total_in_gbp }}
I am using Conditional Expression to sum up all the values per currency.

Django: how to get an object of manager on admin page

I am new to Django and I am working on a project. In my project an admin will have the power to assign the project to a manager. So I want to render the name of all the managers from the database so that it will be displayed to the admin.
here is my .html file where I want to render the name of the manager in:
<div class="body table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>S No.</th>
<th>COMPANY NAME</th>
<th>TEAM MEMBER</th>
<th>EMAIL</th>
<th>ASSIGN TEAM</th>
</tr>
</thead>
<tbody>
{%for team in object%}
<tr>
<form id="form_id" method="POST" action = "{% url 'accept' %}">
{% csrf_token %}
<th scope="row"> {{ forloop.counter }}</th>
<td>{{team.company_name}}</td>
<td>{{team.team_member}}</td>
<td>{{team.email}}</td>
<td>
<select name="manager">
{% for manager in managers %}
<option value ="{{manager.id}}">{{manager.name}}</option>
{% endfor %}
</select>
<!-- </div> -->
</td>
</tr>
{% endfor %}
</tbody>
</table>
Here is my model for manager:
class manager(models.Model):
name = models.CharField(max_length= 500)
designation = models.CharField(max_length= 500)
class Meta:
permissions = [
("edit_task", "can edit the task"),
]
def __str__(self):
return self.name
Here is my views.py;
def accept(request):
obj= Create_Team.objects.filter(status='Accept')
if request.method == 'POST':
acc = manager()
manager_id = int(request.POST.get('manager', 1))
acc.manager = manager.objects.get(pk=manager_id)
return render(request, "admin/accept.html", {"object": obj})
In the admins page, I want to display all the names of the managers. I have added the image of the admin page.
I think you forgot to include the managers queryset in the context variable
def accept(request):
obj= Create_Team.objects.filter(status='Accept')
managers = manager.objects.all()
if request.method == 'POST':
acc = manager()
manager_id = int(request.POST.get('manager', 1))
acc.manager = manager.objects.get(pk=manager_id)
return render(request, "admin/accept.html", {"object": obj, "managers": managers})

Django: Trying to click on a link and remove an assigned client

Good morning. I am having an issue trying to remove a client from an assigned bed. I created a one-item form called "RoomUpdate" that will allow a user to add a client to a bed that is empty via a dropdown through a ModelChoiceField.
When the bed is full, it does not allow the access to the drop down, instead, I have a link that states "remove client." What I want to happen is when I click that button, it assigns the default value of None to that bed in that room.
What's tricky, at least to my new-ish to Django mind, is how I do this through multiple tables. Having looked for multiple answers and tried different things, I know I've lost track of what I'm doing so I definitely could use some help.
models.py
class Room(models.Model):
room_id = models.AutoField(primary_key=True)
room_number = models.CharField(max_length=5)
shelter_id = models.ForeignKey(Shelter)
max_occupancy = models.CharField(max_length=3)
floor_location = models.CharField(max_length=3)
def __str__(self):
return self.room_number
class Bed(models.Model):
bed_id = models.AutoField(primary_key=True)
room_id = models.ForeignKey(Room, related_name='beds')
bed_size = models.ForeignKey(BedSize)
client_assigned = models.ForeignKey(Clients, null=True, blank=True, default=None)
forms.py
class RoomUpdate(forms.ModelForm):
client_assigned = forms.ModelChoiceField(queryset=Clients.objects.all(), required=False)
#def __init__(self, *args, **kwargs):
#super(RoomUpdate, self).__init__(*args, **kwargs)
#self.fields['client_assigned'].choices.insert(0, ('','---------' ) )
class Meta:
model = Room
fields = ( 'client_assigned', )
views.py
def room_update(request, pk, template_name='shelter/room_edit.html'):
rooms = get_object_or_404(Room, pk=pk)
form = RoomUpdate(request.POST or None, instance=rooms)
beds = Bed.objects.filter(room_id=pk)
if form.is_valid():
form.save()
return redirect('/shelter/')
return render(request, template_name, {'form': form, 'rooms': rooms, 'beds':beds,})
def remove_client(request, pk):
rooms = get_object_or_404(Room, pk=pk)
bed = Bed.objects.filter(room_id=pk)
form = RoomUpdate(request.POST)
template_fail = 'clients/invalid_permissions.html'
if request.method=='POST':
if form.is_valid():
bed.objects.update(client_assigned=None)
bed.save()
else:
return redirect(request, template_fail)
return render_to_response(request, {'rooms': rooms, 'bed': bed})
template
<form action="" method="POST">
{% csrf_token %}
<div class="room box-shadow">
<h4>Room {{ rooms.room_number }}</h4>
<table>
{% for i in rooms.beds.all %}
<tr>
<td>Bed ID: </td>
<td>{{i.bed_id }}</td>
</tr>
<tr>
<td>Bed Size: </td>
<td>{{i.bed_size }}</td>
</tr>
<tr>
<td valign="top">Client: </td>
<td>
{% if i.client_assigned %}
{{ i.client_assigned }}
<br \>
Remove Client
{% else %}
{{ form.client_assigned }}
{% endif %}
</td>
</tr>
<tr>
<td colspan="2">
<hr class="style-two" />
</td>
</tr>
{% endfor %}
<tr>
<td><input type="submit" value="Submit" /></td>
<td></td>
</tr>
</table>
</div>
</form>
I've been playing around with this a bit and making some sort of progress. If I change the url from the same as the edit, I get it to work in that it deletes from the table and redirects the user to a new page.
I would prefer it not redirect the user to a new page, but rather, update the page that's there.
Thoughts?
The new view looks like this:
def remove_client(request, pk, template_name='shelter/test.html'):
bed = Bed.objects.filter(bed_id=pk).update(client_assigned=None)
return render(request, template_name, { 'bed':bed })
Further diving into this, I found a solution I rather like and actually works in a way I wanted but couldn't figure out. Instead of focusing on the room, I realized that I needed to focus on the smaller container -- the bed -- and since it can move around, it would be the better choice.
Currently, this functionality allows me to move beds to different rooms, remove clients by selecting the '----' selector, and allows me to add clients.
So here is the answer I came up with:
forms.py
class RoomUpdate(forms.ModelForm):
bed_id = forms.CharField()
room_id = forms.ModelChoiceField(queryset=Room.objects.all())
bed_size = forms.ModelChoiceField(queryset=BedSize.objects.all(), required=False)
client_assigned = forms.ModelChoiceField(queryset=Clients.objects.all(), required=False)
class Meta:
model = Bed
fields = ('bed_id', 'client_assigned', 'room_id', 'bed_size' )
views.py
def room_update(request, pk, template_name='shelter/room_edit.html'):
beds = get_object_or_404(Bed,pk=pk)
form = RoomUpdate(request.POST or None, instance=beds)
if form.is_valid():
form.save()
return redirect('/shelter/')
return render(request, template_name, {'form': form, 'beds':beds,})
room_edit.html
<form action="" method="POST">{% csrf_token %}
<div class="room box-shadow">
<h4>Room </h4>
<table>
<tr>
<td>Room: </td>
<td>{{ form.room_id }}</td>
</tr>
<tr>
<td>Bed ID: </td>
<td>{{ form.bed_id }}</td>
</tr>
<tr>
<td>Bed Size: </td>
<td>{{ form.bed_size }}</td>
</tr>
<tr>
<td valign="top">Client: </td>
<td>{{ form.client_assigned }}</td>
</tr>
<tr>
<td colspan="2"><hr class="style-two" /></td>
</tr>
<tr>
<td><input type="submit" value="Submit" /></td>
<td></td>
</tr>
</table>
</div>
</form>

Categories

Resources