python - Django: Parent Model with multiple child model types -


i've created set of django model cms show series of products.

each page contains series of rows, have generic

class productrow(models.model):   slug = models.slugfield(max_length=100, null=false, blank=false, unique=true, primary_key=true)   name = models.charfield(max_length=200,null=false,blank=false,unique=true)   active = models.booleanfield(default=true, null=false, blank=false) 

then have series of children of model, different types of row:

class productbanner(productrow):   wide_image = models.imagefield(upload_to='product_images/banners/', max_length=100, null=false, blank=false)   top_heading_text = models.charfield(max_length=100, null=false, blank=false)   main_heading_text = models.charfield(max_length=200, null=false, blank=false)   ...  class productmagazinerow(productrow):   title = models.charfield(max_length=50, null=false, blank=false)   show_descriptions = models.booleanfield(null=false, blank=false, default=false)   panel_1_product = models.foreignkey(product, related_name='+', null=false, blank=false)   panel_2_product = models.foreignkey(product, related_name='+', null=false, blank=false)   panel_3_product = models.foreignkey(product, related_name='+', null=false, blank=false)   ...  class producttextgridrow(productrow):   title = models.charfield(max_length=50, null=false, blank=false)   col1_title = models.charfield(max_length=50, null=false, blank=false)   col1_product_1 = models.foreignkey(product, related_name='+', null=false, blank=false)   col1_product_2 = models.foreignkey(product, related_name='+', null=false, blank=false)   col1_product_3 = models.foreignkey(product, related_name='+', null=false, blank=false)   ... 

and on.

then in productpage have series of productrows:

class productpage(models.model):   slug = models.slugfield(max_length=100, null=false, blank=false, unique=true, primary_key=true)   name = models.charfield(max_length=200, null=false, blank=false, unique=true)   title = models.charfield(max_length=80, null=false, blank=false)   description = models.charfield(max_length=80, null=false, blank=false)   row_1 = models.foreignkey(productrow, related_name='+', null=false, blank=false)   row_2 = models.foreignkey(productrow, related_name='+', null=true, blank=true)   row_3 = models.foreignkey(productrow, related_name='+', null=true, blank=true)   row_4 = models.foreignkey(productrow, related_name='+', null=true, blank=true)   row_5 = models.foreignkey(productrow, related_name='+', null=true, blank=true) 

the problem have got, want allow 5 rows in productpage of different child types of productrow. when iterate on them such

in views.py:

product_page_rows = [product_page.row_1,product_page.row_2,product_page.row_3,product_page.row_4,product_page.row_5] 

and in template:

{% row in product_page_rows %}   <pre>{{ row.xxxx }}</pre> {% endfor %} 

i cannot reference child field xxxx.

i tried adding "type()" method both parent , children, try , distinguish class each row is:

class productrow(models.model):    ...    @classmethod   def type(cls):       return "generic" 

and

class producttextgridrow(tourrow):    ...    @classmethod   def type(cls):       return "text-grid" 

but if change xxxx .type() in template shows "generic" every item in list (i had defined variety of row types in data), guess coming productrow rather appropriate child type. can find no way children accessible correct child type rather parent type, or determine child type (i tried catching attributeerror well, didn't help).

can advise how can handle list of varied model types of contain common parent, , able access fields of appropriate child model type?

this (read "always") bad design have this:

class mymodel(models.model):     ...     row_1 = models.foreignkey(...)     row_2 = models.foreignkey(...)     row_3 = models.foreignkey(...)     row_4 = models.foreignkey(...)     row_5 = models.foreignkey(...) 

it not scalable. if ever want allow 6 rows or 4 rows instead of 5, 1 day (who knows?), have add/delete new row , alter database scheme (and handle existing objects had 5 rows). , it's not dry, amount of code depends on number of rows handle , involves lot of copy-pasting.

this become clear bad design if wonder how if had handle 100 rows instead of 5.

you have use manytomanyfield() , custom logic ensure there @ least 1 row, , @ 5 rows.

class productpage(models.model):     ...     rows = models.manytomanyfield(productrow) 

if want rows ordered, can use explicit intermediate model this:

class productpagerow(models.model):      class meta:         order_with_respect_to = 'page'      row = models.foreignkey(productrow)     page = models.foreignkey(productpage)  class productpage(models.model):     ...     rows = model.manytomanyfield(productrow, through=productpagerow) 

i want allow n rows (let's 5), implement own order_with_respect_to logic:

from django.core.validators import maxvaluevalidator  class productpagerow(models.model):      class meta:         unique_together = ('row', 'page', 'ordering')      max_rows = 5      row = models.foreignkey(productrow)     page = models.foreignkey(productpage)     ordering = models.positivesmallintegerfield(         validators=[             maxvaluevalidator(max_rows - 1),         ],     ) 

the tuple ('row', 'page', 'ordering') uniqueness being enforced, , ordering being limited 5 values (from 0 4), there can't more 5 occurrences of couple ('row', 'page').

however, unless have reason make 100% sure there no way add more n rows in database mean (including direct sql query input on dbms console), there no need "lock" level.

it "untrusted" user able update database through html form inputs. , can use formsets force both minimum , maximum number of rows when filling form.

note: applies other models. bunch of fields named foobar_n, n incrementing integer, betrays bad database design.


yet, not fix issue.

the easiest (read "the first comes mind") way child model instance parent model instance loop on each possible child model until instance matches.

class productrow(models.model):     ...     def get_actual_instance(self):         if type(self) != productrow:             # if it's not productrow, child             return self         attr_name = '{}_ptr'.format(productrow._meta.model_name)         possible_class in self.__subclasses__():             field_name = possible_class._meta.get_field(attr_name).related_query_name()             try:                 return getattr(self, field_name)             except possible_class.doesnotexist:                 pass          # if no child found, productrow          return self 

but involves hit database each try. , still not dry. efficient way add field tell type of child:

from django.contrib.contenttypes.models import contenttype  class productrow(models.model):     ...     actual_type = models.foreignkey(contenttype, editable=false)      def save(self, *args, **kwargs):         if self._state.adding:             self.actual_type = contenttype.objects.get_for_model(type(self))          super().save(*args, **kwargs)      def get_actual_instance(self):         my_info = (self._meta.app_label, self._meta.model_name)         actual_info = (self.actual_type.app_label, self.actual_type.model)         if type(self) != productrow or my_info == actual_info:             # if actual instance             return self         # otherwise         attr_name = '{}_ptr_id'.format(productrow._meta.model_name)         return self.actual_type.get_object_for_this_type(**{             attr_name: self.pk,         }) 

Comments

Popular posts from this blog

java - Static nested class instance -

c# - Bluetooth LE CanUpdate Characteristic property -

JavaScript - Replace variable from string in all occurrences -