Suppose I have this model (not real code):
class Message(models.Model):
content = models.CharField(...)
mentions = models.ManyToManyField(User)
content can for instance be:
Say hi to @Adam and @Bernard
During the Message model save(), “Adam” and “Bernard” are extracted from the text, and their respective user instances added to mentions, so that
>>> m = Message.objects.create(content="Say hi to @Adam and @Bernard")
>>> m.mentions.all()
>>> [User: Adam, User: Bernard]
Now, as to my problem:
How do I cope with Bernard performing a sex-change and renaming himself Bernarda? I would very much like the content then to read Say hi to @Adam and @Bernarda.
Basically, I’ve tried a strategy of replacing names with ID’s when saving (Say hi to @Adam and @Bernard then becomes Say hi to @1 and @2, i.e. their User.pk’s). This works fine when saving, and OK-ish when reading.
Doing this replacement in the model’s save() method works fine, but read and lookup operations are a major problem. I cannot find an easy way of making sure that what is stored in the database (ie Say hi to @1 and @2) ‘magically’ becomes Say hi to @Adam and @Bernard whenever it is being queried (be that through .filter() and related operations) or its instance being evaluated.
I’m unsure whether I’m on the right track here; any pointers or ideas to doing this differently are very welcome!
Write a custom model field class, that extends CharField, you should overread the documentation first so that you have an idea of what’s there,
that metaclass = models.SubfieldBase is required for the custom Field to call the custom to_python method
Override to_python(), it takes a database value and should return a python value, there you can replace @1 by the username of User #1 (say @Adam or @Bernarda),
Override get_prep_value(), it takes a python value and should return a value for the SQL, there you can replace @Adam by the pk of User Admin (say @1),
Query on mentions, for example
Message.objects.filter(mention=someuser), or make another field: one that contains the replaced version and one that contains the possibly out of date usernames – i don’t recommend the latterYou could also query on content, for example
Message.objects.filter(content__contains='@1')Voodoo doesn’t do any good in general, i would make a model method
get_content_display()that does the replacement, and a templatetag that displays the html version of the content – since your probably want to link @Adam to his user page anyway. I would stick to this because that’s KISS.CREDITS to LaundroMat for debugging this answer and fixing it (there was 2 errors and 1 inaccuracy, which he debugged and came back to report !!!). Also DO UPVOTE HIS COMMENT unless you have better way to reward him. Rock’on.