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
- Reusing Django’s filter_horizontal
- Just as with pop-ups, it’s possible to use the built-in JavaScript for filtering multiple-selection popups on custom forms.
- Django formsets and date/time fields
- Date/Time fields in Django formsets appear to have incompatible default values, resulting in forms using them always looking as though they’ve got a new entry when they don’t.
- Django tutorial mostly ready
- My long-promised Django tutorial is pretty much ready. It’s still designed around an in-person tutorial, but you should be able to get started using it even if you’re on your own.
- Django: Beyond the SQL
- Django is a great application framework for Python and web applications. You can use it to greatly speed up your database and application development both on the web and on the command line. This tutorial is currently a very rough draft; it probably won’t be very useful without the assistance of someone who knows Django running the tutorial. If I ever run this tutorial a second time, I’ll probably update it with screenshots to make it more usable for individuals.
- Django actions as their own intermediate page
- The Django documentation recommends a complicated URL redirect to handle admin actions that need an intermediate page. But there’s no reason we can’t just piggy-back the intermediate page on the action itself.
- 22 more pages with the topic Django, and other related pages
More Python
- PyTown
- General rambling in code regarding Python, Mailman, and Django.
- Thinking Python: Django cache expiration time
- Django sets the expiration time when data is cached. Sometimes it makes more sense to expire data dynamically based on later changes to the database. Does this mean a change to CacheClass? Not necessarily.
- Django Twitter tag and RSS object
- I wanted to embed my twitter feed into my Django blog, and didn’t see any simple RSS readers for Python that did what I wanted.
- Excerpting partial XHTML using minidom
- You can use xml.dom.minidom to parse partial XHTML as long as you use a few tricks and don’t mind that getElementById doesn’t work.
- Media duration in Python on Mac OS X
- It turns out to be very easy to get the duration of MP3 files, MPEGs, and other media files on the OS X command line.
- 18 more pages with the topic Python, and other related pages
