[Answered ]-How to DRY up my models

2đź‘Ť

âś…

You can use a one-to-one relation the other way around:

class Vote(models.Model):
    voter = models.ForeignKey(User)
    ...# some additional fields

class Question(models.Model):
    title = models.CharField(max_length=150)
    detail = models.TextField()
    submitter = models.ForeignKey(User)
    date_added = models.DateTimeField(auto_now_add = True)
    vote = models.OneToOneField(Vote)
    ...# some additional fields such as tags

class Answer(models.Model):
    detail = models.TextField()
    submitter = models.ForeignKey(User)
    date_added = models.DateTimeField(auto_now_add = True)
    vote = models.OneToOneField(Vote)
    ...
👤Selcuk

0đź‘Ť

Have a look at abstract base classes

You can put the common fields/functionality in there (submitter, detail, etc) and then inherit it in your Question and Answer models

Edit:

Based on the other answers and me looking at your problem a bit more I’m changing my answer. You’re currently walking the line between “DRY” and “good readability/maintainability” and different developers come down on different sites of the line, so you’re likely just going to have to pick the one you like best.

On that note, here’s how I’d lay it it out:

##############################
# Question and Answer Models #
##############################

class QuestionAnswerBase(models.Model): # Choose a better name :)
    detail = models.TextField()
    submitter = models.ForeignKey(User)
    date_added = models.DateTimeField(auto_now_add = True)
    ... # More common fields here

    class Meta:
        abstract = True


class Question(QuestionAnswerBase):
    title = models.CharField(max_length=150)
    ...# some additional fields such as tags etc


class Answer(QuestionAnswerBase):
    ... # No extra fields needed but you'll prob want a custom __unicode__ method etc


###############
# Vote Models #
###############

class VoteBase(models.Model):
    voter = models.ForeignKey(User)
    ... # Other shared fields etc

    class Meta:
        abstract = True

class AnswerVote(VoteBase):
    answer = models.ForeignKey(Answer)


class QuestionVote(VoteBase):
    question = models.ForeignKey(Question)

(comment blocks are for readability in this instance.)

“But now I have even more models!”

Yes you do, but there are no duplicated fields on any of them, and the abstraction means you have the power to add different behaviour to- for example- a QuestionVote and an AnswerVote (say you want the ability to retract AnswerVotes if a better Answer comes along, but don’t want to be able to retract QuestionVotes) without the need to have “Is this X a Y or a Z? If it’s a Z do this else do this” clauses in every model method you write.

“It looks ugly!”

It’s both readable and explicit, which means that it’s beautiful 🙂

“I want to do it another way!”

That’s fine, there are already other good answers that show how to do that. This is just my suggestion as someone that is quite keen on best practices and readable code.

👤ptr

0đź‘Ť

I personally feel that DRY is pretty important, so I would be willing to sacrifice some elegance to achieve it. For example, in this case, I might do this:

class QuestionOrAnswer(models.Model):
    is_question = models.BooleanField()
    title = models.CharField(max_length=150)
    detail = models.TextField()
    submitter = models.ForeignKey(User)
    date_added = models.DateTimeField(auto_now_add = True)
    question_specific_field = ...

    def clean(self):
        # Make sure that question_specific_field is set only if is_question is true.

class Vote(models.Model):
    target = models.ForeignKey(QuestionOrAnswer)
    ...
👤DavidM

Leave a comment