Multiple Input Fields with multiple inheritance
Here’s a very simplified version of our Django task management system:
[toggle code]
-
class Task(models.Model):
- title = models.CharField(max_length=100)
- parent = models.ForeignKey('self', blank=True, null=True)
- details = models.TextField(blank=True)
- step = models.PositiveIntegerField(default=5, choices=zip(range(100), range(100)))
The details of the task are stored in a TextField; many tasks have subtasks. When editing a task, we also often want to edit the titles, step, and details of the immediate subtasks. so we display them as a series of lines using inlineformset_factory. By default, Django’s forms will display a TextField as a Textarea. If there’s more than one subtask, textareas are too big; they defeat the purpose of having a simple means of viewing and editing subtasks all on one screen, because they won’t be all on one screen.
So we use a special ModelForm which overrides Django and tells it to use a CharField for that field:
[toggle code]
- #for editing tasks inline in a project
-
class taskForm(forms.ModelForm):
- details = forms.CharField(required=False)
Up until a few weeks ago, this worked fine. While we weren’t allowed to enter multiple lines in an “input type=text” field, they accepted data that already had multiple lines without mangling it. Recently, however, Safari has started double-checking the data, and it’s been collapsing multiple lines of data into one, which, when we save the task, ends up collapsing the lines in the database as well. It’s hard to complain about what Safari is doing here; it almost certainly should be doing that.
My initial suggestion (other than give in and use a textarea for every line) was to block editing the details if the details already contained multiple lines. But as I was taking a look at it, I wondered if this was a job for multiple inheritance. Could I make a class inherit from both TextInput and Textarea, and route the “render” method to create a textarea when necessary, and a single-line input otherwise?
Python does support multiple inheritance. But there’s no obvious way to tell super() to specifically use one or another of the ancestors. A quick Google search brought up Using Mix-ins with Python. The way to alter the order of ancestor searches in Python is to modify an attribute called “__bases__”. It’s a tuple of all of the ancestor classes, and Python searches the classes in that tuple in order to determine which method to use. By altering the order of the tuple, we can alter the method resolution order.
[toggle code]
- #detail fields need to be a textarea if they contain multiple lines
- #but we'd prefer them to be a standard text-input when possible
-
class CharOrTextInput(forms.TextInput, forms.Textarea):
-
def render(self, name, value, *args, **kwargs):
-
if value:
- value = value.strip()
-
if not value or value.find("\n") == -1:
- CharOrTextInput.__bases__ = (forms.TextInput, forms.Textarea)
-
else:
- CharOrTextInput.__bases__ = (forms.Textarea, forms.TextInput)
- return super(CharOrTextInput, self).render(name, value, *args, **kwargs)
-
if value:
-
def render(self, name, value, *args, **kwargs):
- #for editing tasks inline in a project
-
class taskForm(forms.ModelForm):
- details = forms.CharField(required=False, widget=CharOrTextInput)
This overrides the render method to render as Textarea if there are multiple lines, or as TextInput when there are not.
Note that you don’t modify __bases__ on self but on the class definition itself. Also, this is the first thing I’ve done with multiple inheritance, so it could easily be all wrong.
- The Python 2.3 Method Resolution Order: Michele Simionato
- Why am I linking to the Python 2.3 documentation in 2009? Because the Python 2.6.4 documentation also links to it. “As usual in these circumstances, I warn the reader that what follows should be correct, but I don't give any warranty. Use it at your own risk and peril.”
- Python Classes
- “Taken together, these properties make it possible to design reliable and extensible classes with multiple inheritance.”
- Using Mix-ins with Python: Chuck Esterbrook
- “Python classes have an attribute named __bases__, which is a tuple of their base classes. Consistent with Python design, you can play with it at runtime.”
More Django
- Converting an existing Django model to Django-MPTT
- Using a SQL database to mimic a filesystem will, eventually, create bottlenecks when it comes to traversing the filesystem. One solution is modified preordered tree traversal, which saves the tree structure in an easily-used manner inside the model.
- Two search bookmarklets for Django
- Bookmarklets—JavaScript code in a bookmark—can make working with big Django databases much easier.
- Fixing Django’s feed generator without hacking Django
- It looks like it’s going to be a while before the RSS feed generator in Django is going to get fixed, so I looked into subclassing as a way of getting a working guid in my Django RSS feeds.
- ModelForms and FormViews
- This is just a notice because when I did a search, nothing came up. Don’t use ModelForm with FormView, use UpdateView instead.
- Django: fix_ampersands and abbreviations
- The fix_ampersands filter will miss some cases where ampersands need to be replaced.
- 29 more pages with the topic Django, and other related pages
More Python
- Quick-and-dirty old-school island script
- Here’s a Python-based island generator using the tables from the Judges Guild Island Book 1.
- Astounding Scripts on Monterey
- Monterey removes Python 2, which means that you’ll need to replace it if you’re still using any Python 2 scripts; there’s also a minor change with Layer Windows and GraphicConverter.
- Goodreads: What books did I read last week and last month?
- I occasionally want to look in Goodreads for what I read last month or last week, and that currently means sorting by date read and counting down to the beginning and end of the period in question. This Python script will do that search on an exported Goodreads csv file.
- Test classes and objects in python
- One of the advantages of object-oriented programming is that objects can masquerade as each other.
- Timeout class with retry in Python
- In Paramiko’s ssh client, timeouts don’t seem to work; a signal can handle this—and then can also perform a retry.
- 30 more pages with the topic Python, and other related pages