I have a few models that have pairs of fields named like:
The former one(s) can contain LaTeX formulas, and what I’m doing is I’m saving those formulas already rendered (via server-side mathjax) as svgs in the field(s) whose name begins with
rendered_. This makes things way easier on the frontend. Since I have several models that implement this behavior and on differently named fields, I tried to abstract away this behavior and I thought I could use an abstract model for this. This is what I came up with.
class TexRenderable(models.Model): class Meta: abstract = True def _get_renderable_field_pairs(self): ret =  for field in self._meta.get_fields(): split_field_name = field.name.split("_", maxsplit=1) if split_field_name == "rendered": # this is a target field target_field = field.name # look for a field with the same name minus the `rendered_` prefix source_field = self._meta.get_field(split_field_name) # target field will contain rendered text from this field ret.append( (source_field, target_field), ) return ret def save(self, *args, **kwargs): creating = self.pk is None renderable_field_pairs = self._get_renderable_field_pairs() for (source, target) in renderable_field_pairs: # only re-render if the field's value has changed value_changed = creating or getattr( type(self).objects.get(pk=self.pk), source ) != getattr(self, source) if value_changed: rendered_content = get_rendered_tex(getattr(self, source)) setattr(self, target, rendered_content) return super(TexRenderable, self).save(*args, **kwargs)
I’d like to hear your thoughts on this approach.
First of all, is this a good use case for abstract model inheritance? I was thinking maybe a mixin would be more appropriate given there are no fields defined inside of the base model class, but I don’t have much experience with mixins in python so I’m not sure.
Secondly, how does the actual implementation of the method look? There are a few things that feel hacky to me, like calling
type(self) to get the model class. I haven’t worked with complex inheritance-related stuff in python, so I don’t (currently) know any better.
Say I want a concrete model to inherit from
TexRenderable as well as this other abstract model:
class AbstractItem(models.Model): course = models.ForeignKey(Course) topic = models.ForeignKey(Topic) class Meta: abstract = True def save(self, *args, **kwargs): if self.topic not in self.course.topics.all(): raise ValidationError("Chosen topic doesn't belong to chosen course") return super(Item, self).save(*args, **kwargs)
this simply adds two foreign keys and does a check inside of
save before actually saving. What would be the correct order to put them inside of the parentheses when declaring the child class? Does it even matter, or will the behavior of
save be the same regardless? Will this even work, or will I get an error when calling
save from a child instance? I know I could just try it out but this is a novel topic for me, so I really want to grasp how multiple inheritance works in python. I’ll do my research on the side too–if you feel this part is off-topic, just ignore it.