I have a setup where the user can define products (like 'MacBook Pro'). Each Product
gets "instantiated" to create a physical item (ProductInstance
) which gets a serial number. Pretty simple so far.
I want the user to be able to define other types of unique identifiers, like MAC addresses, VIN, social security number, etc. I call this a UIDType
. Each Product can have zero or more of any UIDType
. These need to be differentiated somehow by assigning a name/description and linking to both the Product
and the UIDType
(UIDProductInfo
). To actually assign values I need to assign a value to a UIDProductInfo
for a specific ProductInstance
-- this is done with the UID
model.
When the user instantiates a product (creates a ProductInstance
), they should be forced to create one and only one UID
for each UIDProductInfo
of the Product
they're instantiating. I'd like to do it all on a single page. Here's the question: Using a CreateView
, how do I do this?
I think I need a ModelForm
for the ProductInstance
and a formset to handle the UIDs. The forms in the UID
formset need to create a UID
that's bound to each of the UIDProductInfo
objects associated with the Product
and to the newly created ProductInstance
. Unfortunately, I have no idea how to do that. I tried using the initial
parameter when creating the formset in the CreateView
's get_context_data
method, and a few other more kludgey approaches but I just can't get the formset created properly.
Thanks for your help. The models and my sad attempt at the CreateView.get_context_data
method are below
models:
# a product, i.e. Toyota Corolla
class Product(models.Model):
name = models.CharField(max_length = 255)
identifiers = models.ManyToManyField('UIDType', related_name = 'products', through = 'UIDProductInfo')
# a physical item, an "instantiation" of a Product. Gets a serial number
class ProductInstance(models.Model):
serial_number = models.CharField(max_length = 255)
product = models.ForeignKey(Product, related_name = 'instances')
# a type of unique identifier, like a MAC address
class UIDType(models.Model):
name = models.CharField(max_length = 255, unique = True)
# a description of a UIDType as it applies to a particular Product.
# for example, a computer might get two MAC addresses, 'Host MAC 1' and 'Host MAC 2'
class UIDProductInfo(models.Model):
name = models.CharField(max_length = 255)
type = models.ForeignKey(UIDType, related_name = 'info')
product = models.ForeignKey(Product, related_name = 'product_identifiers')
# a unique identifier for a ProductInstance
class UID(models.Model):
value = models.CharField(max_length = 255)
info = models.ForeignKey(UIDProductInfo, related_name = 'values')
product_instance = models.ForeignKey(ProductInstance, related_name = 'identifiers')
get_context_data:
def get_context_data(self, **kwargs):
self.object = None
context = super(ProductInstanceCreate, self).get_context_data(**kwargs)
product = get_object_or_404(Product, id = self.kwargs.get('product_pk'))
uid_info = [ ]
pi = ProductInstance(product = product)
for info in pi.product.product_identifiers.all():
uid = UID(productInstance = pi, id_type = info)
uid_info.append({'info':uid})
UIDFormset = inlineformset_factory(ProductInstance, UID,
fields = ('identifier', 'info',), can_delete = False,
widgets = { 'info' : HiddenInput() },
labels = {'identifier' : ''}, extra = 3)
uid_formset = UIDFormset(
instance = pi,
initial = uid_info
)
context['identifiers_formset'] = uid_formset
return context
I've solved this problem using the technique described here rather than using a formset:
https://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/