Reputation: 689
I want to show a form within another form.
For example I have these models:
class Measure(models.Model):
length = models.ForeignKey(Statistics, related_name='Length', null=True, blank=True)
surface_area = models.ForeignKey(Statistics, related_name='Surface area+', null=True, blank=True)
section_area = models.ForeignKey(Statistics, related_name='Section area+', null=True, blank=True)
volume = models.ForeignKey(Statistics, related_name='Volume', null=True, blank=True)
diameter = models.ForeignKey(Statistics, related_name='Diameter', null=True, blank=True)
class Statistics(models.Model):
total = models.FloatField('Total', null=True, blank=True)
avg = models.FloatField('Average', null=True, blank=True)
min = models.FloatField('Minimum', null=True, blank=True)
max = models.FloatField('Maximum', null=True, blank=True)
standard_deviation = models.FloatField('Standard deviation', null=True, blank=True)
and then I have these forms corresponding to the previous models:
class StatisticsForm(forms.ModelForm):
class Meta:
model = Statistics
fields = '__all__'
class MeasureForm(forms.ModelForm):
class Meta:
model = Measure
fields = '__all__'
# Here I have to say that the right form for each field is the StatisticForm
The ForeignKey in the forms is rendered as a combo box includes all the objects in the other table (in my case the Statistics table), I want to replace the combo box with an object of StatisticsForm so I can control the way I render the Statistics objects
Thank you very much.
Upvotes: 0
Views: 48
Reputation: 17526
Your database scheme and models are incorrectly designed to solve the problem at hand. You are defining a "has many" relationship in the wrong direction. One Measurement
is supposed to have several Statistics
, however one Statistics
is not supposed to have many Measurement
.
As your models are set up right now the ForeignKey
is on the wrong side of the relationship. You should do this instead:
class Measure(models.Model):
def save(self,*args,**kwargs):
result = super(Measure, self).save(*args,**kwargs)
Statistics.objects.create(name='length', measurement=self)
Statistics.objects.create(name='section', measurement=self)
Statistics.objects.create(name='surface', measurement=self)
Statistics.objects.create(name='volume', measurement=self)
Statistics.objects.create(name='diameter', measurement=self)
return result
To provide the same comfort in accessing the Statistics
s for one Measurement
as in your current code you can add a couple of @property
shortcuts:
class Measure(models.Model):
@property
def length(self):
return self.statistics_set.get(name='length')
@property
def section(self):
return self.statistics_set.get(name='section')
@property
def surface(self):
return self.statistics_set.get(name='surface')
@property
def volume(self):
return self.statistics_set.get(name='volume')
@property
def diameter(self):
return self.statistics_set.get(name='diameter')
class Statistics(models.Model):
name = models.CharField(max_length=10)
measurement = models.ForeignKey('Measurement')
total = models.FloatField('Total', null=True, blank=True)
avg = models.FloatField('Average', null=True, blank=True)
min = models.FloatField('Minimum', null=True, blank=True)
max = models.FloatField('Maximum', null=True, blank=True)
standard_deviation = models.FloatField('Standard deviation', null=True, blank=True)
Once you fix the relationship between the objects the problem becomes much easier to solve. Instead of being ForeignKeyFields
in the form, the Statistics
s become proper related objects, which are routinely handled by django.
As @solarisssmoke mentioned in the comments you are looking for formsets. Here is an example from the django documentation showing how to achieve what you need:
The models in question:
class Author(models.Model):
name = models.CharField(max_length=100)
title = models.CharField(max_length=3, choices=TITLE_CHOICES)
birth_date = models.DateField(blank=True, null=True)
def __str__(self): # __unicode__ on Python 2
return self.name
class Book(models.Model):
name = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
And a view using inlineformset_factory
to create the needed formset:
from django.forms import inlineformset_factory
def manage_books(request, author_id):
author = Author.objects.get(pk=author_id)
BookInlineFormSet = inlineformset_factory(Author, Book, fields=('title',))
if request.method == "POST":
formset = BookInlineFormSet(request.POST, request.FILES, instance=author)
if formset.is_valid():
formset.save()
# Do something. Should generally end with a redirect. For example:
return HttpResponseRedirect(author.get_absolute_url())
else:
formset = BookInlineFormSet(instance=author)
return render(request, 'manage_books.html', {'formset': formset})
If performance becomes an issue also have a look at prefetch_related
for boosting performance.
Upvotes: 2