models.py
class Playlist(models.Model):
name = models.CharField(max_length=50)
num_of_songs = models.IntegerField(max_length=15)
duration = models.IntegerField()
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
songs = models.ManyToManyField("Song", blank=True)
forms.py
class PlaylistEditForm(forms.ModelForm):
class Meta:
model = Playlist
fields = ['name', 'songs']
I calculate duration and num_of_songs based on the songs I get from the form. But I do that calculation from a view.
views.py
playlist = Playlist.objects.get(id=playlist_id)
if request.method == 'POST':
form = PlaylistEditForm(request.POST, instance=playlist)
if form.is_valid():
form.save()
playlist.duration = playlist.songs.aggregate(Sum('duration'))['duration__sum'] or 0
playlist.num_of_songs = playlist.songs.count()
playlist.save()
I want to calculate duration and num_of_songs inside a form.
you can move the calculation to the form overriding the form's save method
def save(self, commit=True):
instance = super(PlaylistEditForm, self).save(commit=False)
instance.duration = instance.songs.aggregate(Sum('duration'))['duration__sum'] or 0
instance.num_of_songs = instance.songs.count()
if commit:
instance.save()
return instance
and your view becomes
playlist = Playlist.objects.get(id=playlist_id)
if request.method == 'POST':
form = PlaylistEditForm(request.POST, instance=playlist)
if form.is_valid():
form.save()
Please refer to Django's official docs for further info.
Is there any reason not to do it in the models save() method? If you have it in the forms save() method, the duration and num_of_songs in the database will not get updated if you save the model instance other than from the modelform.
class Playlist(models.Model):
name = models.CharField(max_length=50)
num_of_songs = models.IntegerField(max_length=15)
duration = models.IntegerField()
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
songs = models.ManyToManyField("Song", blank=True)
def save(self, *args, **kwargs):
if self.pk:
self.duration = self.songs.aggregate(Sum('duration'))['duration__sum'] or 0
self.num_of_songs = self.songs.count()
return super(Playlist, self).save(*args, **kwargs)
You view would be:
if request.method == 'POST':
form = PlaylistEditForm(request.POST, instance=playlist)
if form.is_valid():
playlist = form.save()
Related
I have a view where the user inserts a number of units and once submit button is clicked the excel file is exported as xlsx file. I am using Django import-export to do that BUT I don't know how to filter the CERequests model so the user sees only what (s)he has just inserted. I have implemented the filtering by the user but when clicking submits button it filters all items by the current user but it shows all of them (also items from the past).
What I want is to export only values from the current formset or formsets. What I tried is to put created=created in filter method but it gives me an empty Excel file. When I remove it gives me a list of all CERquests that the user inserted.
What do I need to do to get only data from the current formset(s)?
views.py
class CostCalculator(LoginRequiredMixin, TemplateView):
template_name = 'calculator/cost_calculator.html'
def get(self, *args, **kwargs):
# Create an instance of the formset
formset = CalculatorForm(initial=[{
'author': self.request.user.email,
}])
return self.render_to_response({'ce_request_formset': formset})
# Define method to handle POST request
def post(self, *args, **kwargs):
formset = CalculatorForm(data=self.request.POST)
# Check if submitted forms are valid
if formset.is_valid():
for form in formset:
related_product = form.cleaned_data.get('related_product')
created = form.cleaned_data.get('created')
form.save()
qs = CERequest.objects.filter(related_product__title=related_product, created=created)
dataset = CERequestResource().export(qs)
response = HttpResponse(dataset.xlsx, content_type="xlsx")
response['Content-Disposition'] = 'attachment; filename=filename.xlsx'
return response
return self.render_to_response({'ce_request_formset': formset})
forms.py
class CalculatorForm(forms.ModelForm):
author = forms.CharField(required = False)
number_of_units = forms.IntegerField(help_text='Only numeric values are allowed.', min_value=0)
total_price = forms.IntegerField(widget = forms.HiddenInput(), required = False)
created = forms.DateTimeField(widget = forms.HiddenInput(), required = False)
class Meta:
model = CERequest
fields = ('author', 'related_product', 'related_component', 'number_of_units', 'total_price')
readonly_fields = ('created')
CalculatorForm = formset_factory(CalculatorForm)
models.py
class CERequest(models.Model):
author = models.CharField(max_length=255, blank=True, null=True)
related_component = models.ForeignKey(CostCalculator, on_delete=models.CASCADE, blank=True, null=True)
number_of_units = models.IntegerField(default=0)
related_product = models.ForeignKey(Product, on_delete=models.CASCADE, null=True, blank=True)
created = models.DateTimeField(auto_now=True)
total_price = models.IntegerField(default=0, blank=True, null=True)
I have two django models which are :
class Dataset(models.Model):
name = models.CharField(max_length = 200)
description = models.CharField(max_length=1000)
owner = models.ForeignKey(Profile, null=True, on_delete=models.SET_NULL)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Source(models.Model):
name = models.CharField(max_length = 200)
description = models.CharField(max_length=1000)
dataset = models.ForeignKey(Dataset, null=True, on_delete=models.SET_NULL)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
When saving a Source, I would like to initiate the value of the corresponding Dataset. I tried to initiate the value of my form as suggested here : foreign key as initial value not passed to the ModelForm in django
def create_source(request, dataset_id):
user = request.user
dataset = Dataset.objects.get(id=dataset_id)
form = SourceForm(initial={"dataset" : dataset, })
if request.method == "POST":
form = SourceForm(request.POST or None, initial={"dataset" : dataset, })
if form.is_valid():
source = form.save()
# dataset.source_set.add(source) # Only works if I add this line
return redirect("source", dataset_id=dataset_id, source_id=source.id)
context = {"form": form}
return render(request, "sources/source_form.html", context)
The SourceForm:
class SourceForm(ModelForm):
class Meta:
model = Source
fields = "__all__"
exclude = ["dataset"]
The suggested way does not work. I was able to achieve the desired result by adding the commented line above. It is not a recommended solution since it makes a second call to the database.
Any idea how to give properly the dataset object to the source ?
Passing values in initial for excluded fields does not do anything. Instead what you can do is modify the instance wrapped by the form before saving the it:
def create_source(request, dataset_id):
user = request.user
dataset = Dataset.objects.get(id=dataset_id)
form = SourceForm() # No initial
if request.method == "POST":
form = SourceForm(request.POST) # this is a submitted form `request.POST or None` makes no sense use only `request.POST`
if form.is_valid():
form.instance.dataset = dataset
source = form.save()
return redirect("source", dataset_id=dataset_id, source_id=source.id)
context = {"form": form}
return render(request, "sources/source_form.html", context)
I have this two models in my models.py archive:
class Ticket(models.Model):
name = models.CharField(max_length=50)
description_issue = models.CharField(max_length=1000)
pub_date = models.DateTimeField("publication date", default=timezone.now)
class PossibleSolution(models.Model):
title = models.CharField(max_length=50)
description_solution = models.CharField(max_length=1000)
final = models.BooleanField()
sol_date = models.DateTimeField("solution date", default=timezone.now)
tickets = models.ForeignKey(Ticket, blank=True, null=True)
Example: I have a Ticket, with a name, description and the publication date, and then I create a PossibleSolution, and in the form from which I create the PossibleSolution, I choose the related ticket.
Then, in another form, I update the original Ticket, changing it's name, and then I lose the PossibleSolution I created.
What can be happening here?
EDIT: This is the from from where I edit the Ticket
class TicketForm(forms.ModelForm):
class Meta:
def __init__(self, *args, **kwargs):
super(TicketForm, self).__init__(*args, **kwargs)
self.fields['pub_date'].widget = widgets.AdminSplitDateTime()
self.fields['closing_date'].widget = widgets.AdminSplitDateTime()
self.fields['issuer'].widget.attrs['readonly'] = True
model = Ticket
fields = '__all__'
labels = {
'name': _('Nombre'),
'description_issue': _('Descripción'),
'pub_date': _('Fecha de creacion'),
'closing_date': _('Fecha de cierre'),
'priority': _('Prioridad'),
'issuer': _('Creado por'),
'category': _('Categoría')
}
widgets = {
'pub_date': DateTimeWidget(attrs={'id': "pub_date"}, usel10n=True, bootstrap_version=3),
'closing_date': DateTimeWidget(attrs={'id': "closing_date"}, usel10n=True, bootstrap_version=3),
}
And this is the view where I have the POST
def ticket(request, id_ticket):
if request.method == "POST":
form = TicketForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.save()
messages.success(request, 'ticket-saved')
if id_ticket and int(id_ticket) > 0:
# I have this because I don't know how to really modify
Ticket.objects.get(id=id_ticket).delete()
return redirect('index')
Remove the line which performs the delete query,
def ticket(request, id_ticket):
ticket = Ticket.objects.get(id=id_ticket)
if request.method == "POST":
form = TicketForm(request.POST, instance=ticket)
if form.is_valid():
post = form.save(commit=False)
post.save()
messages.success(request, 'ticket-saved')
return redirect('index')
else:
form = TicketForm(instance=ticket)
return render(request, 'template_name', {'form':form})
Python, Django.
I'm trying to create a form for inserting data into database. Getting error while trying to add a new customers site via form:
ValueError at /new_site/ Cannot assign "[]":
"Sites.customers_id" must be a "Customers" instance.
Model:
class Customers(models.Model):
id = models.AutoField(primary_key=True)
author = models.ForeignKey('auth.User', null=True)
name = models.CharField(max_length=100)
description = models.TextField()
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "Customers"
class Sites(models.Model):
id = models.AutoField(primary_key=True)
customers_id = models.ForeignKey(Customers,null=True)
author = models.ForeignKey('auth.User',blank=True, null=True)
adress = models.CharField(max_length=100, help_text="Example: http://stackoverflow.com/")
s_login = models.CharField(max_length=100, blank=True, default='', help_text="Login and password if required." )
s_password = models.CharField(max_length=100, blank=True, default='')
certificate = models.CharField(max_length=100,blank=True, default='',help_text="File's name")
def __str__(self):
return self.adress
class Meta:
verbose_name_plural = "Sites"
Forms:
class SitesForm(forms.ModelForm):
customers_id = forms.ModelMultipleChoiceField(queryset=Customers.objects.filter(author_id=1))
adress = forms.CharField(max_length=100, help_text="Example: http://www.stackoverflow.com/")
s_login = forms.CharField(max_length=100, required=False, help_text="Login and password if required.")
s_password = forms.CharField(max_length=100, required=False)
certificate = forms.CharField(max_length=100, required=False, help_text="File's name if required")
class Meta:
model = Sites
fields = ( 'customers_id','adress','s_login', 's_password', 'certificate')
def __init__(self, user, *args, **kwargs):
self.user = user
super(SitesForm,self).__init__(*args, **kwargs)
cust = Customers.objects.filter(author_id=user.id)
self.fields['customers_id'].queryset = cust
View:
def new_site(request):
if request.method == 'POST':
form = SitesForm( request.user, request.POST)
if form.is_valid():
site = form.save(commit=False)
site.author = request.user
cusomer.customers_id = request.customers_id
site.save()
return redirect('/customers/')
else:
form = SitesForm( request.user)
return render(request, 'checker/new_site.html', {'form': form, 'username': auth.get_user(request).username })
In views.py
replace
cusomer.customers_id = request.customers_id
with
cusomer.customers_id = Customers.objects.filter(id=request.customers_id)[0]
it will save object of Customer instead of id.
Edit your view like this,
def new_site(request):
if request.method == 'POST':
form = SitesForm( request.user, request.POST)
if form.is_valid():
site = form.save(commit=False)
site.author = request.user
site.customers_id = Customers.objects.get(author=request.user)
site.save()
return redirect('/customers/')
else:
form = SitesForm( request.user)
return render(request, 'checker/new_site.html', {'form': form, 'username': auth.get_user(request).username })
Problem solved.
In views,problem was in this line:
cusomer.customers_id = request.customers_id
Changed to:
site.customers_id = Customers.objects.filter(id=request.POST.get('customers_id'))[0]
And changes in form:
class Meta:
model = Sites
fields = ( 'customers_id','adress','s_login', 's_password', 'certificate')
So you need to remove 'customers_id'. And it looks like:
class Meta:
model = Sites
fields = ('adress','s_login', 's_password', 'certificate')
Thanks everyone!
I have a form that takes information about an item and saves it into the database. Im trying to allow users to edit that form with new/different information and save it again. Im having some difficulty trying to get this to work. I think the problem is when Django validates the data it sees that the slug and unique id of the item already exist and doesnt allow it to validate the data but im not completely sure about this as well. Would really appreciate the help. Thanks.
#view
def edit_item(request, item_id):
if request.method == 'POST':
item = Item.objects.get(id=item_id)
form = AddItem(request.POST,instance=item)
if form.is_valid():
item = form.save(commit=False)
item.user = request.user
item.is_active = True
item.slug = slugify(item.name)
item.save()
return HttpResponseRedirect('thanks.html')
else:
form = AddItem(instance=item )
return render_to_response('forsale.html', locals(), context_instance=RequestContext(request))
#form
class AddItem(forms.ModelForm):
name = forms.CharField(label="Title")
class Meta:
model = Item
exclude = ('user','slug','is_active',)
#model
class Item(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=30)
slug = models.SlugField(max_length=50, unique=True)
is_active = models.BooleanField(default=True, blank=True)
image1 = models.ImageField(upload_to='img')
image2 = models.ImageField(upload_to='img', blank=True)
image3 = models.ImageField(upload_to='img', blank=True)
image_caption1 = models.CharField(max_length=200, blank=True)
image_caption2 = models.CharField(max_length=200, blank=True)
image_caption3 = models.CharField(max_length=200, blank=True)
price = models.DecimalField(max_digits=8, decimal_places=2)
quantity = models.IntegerField(default=1)
description = models.TextField()
created = models.DateTimeField(auto_now_add=True)
shipping_price = models.DecimalField(decimal_places=2, max_digits=6)
categories = models.ManyToManyField(Category)
def save(self, *args, **kwargs):
super(Item, self).save(*args, **kwargs)
if not self.slug:
self.slug = slugify(self.product.title) + "-" + str(self.id)
self.save()
Update your view function like this to return form for get request as well:
def edit_item(request, item_id):
if request.method == 'POST':
item = Item.objects.get(id=item_id)
....
#your existing code
else: #if its GET request
item = Item.objects.get(id=item_id)
form = AddItem(instance=item )
return render_to_response('forsale.html', locals(),
context_instance=RequestContext(request))
Note: you need to handle case when item with item_id does not exists in the DB. In that case do not use instance parameter to instantiate the form.