Django - 在模型中动态创建字段名称(不保存的字段)

时间:2013-07-09 22:25:13

标签: django django-models

我正在尝试实施一个投票系统来跟踪我网站上每种类型用户的投票。我的计划是创建一个投票模型,跟踪每个用户类型的Up投票和总投票,并计算Up投票的百分比。

硬编码看起来像这样:

class Eduuser(AbstractUser):
    TYPE_1 = 'TY1'
    TYPE_2 = 'TY2'
    ...

   USER_TYPE_CHOICES = (
       (TYPE_1, 'Type 1'),
       (TYPE_2, 'Type 2'),
       ...
   )
   user_type = models.CharField(max_length=3, choices=USER_TYPE_CHOICES)


class Vote(models.Model):

    a = models.IntegerField(
        default=0, name=getattr(Eduuser, 'TYPE_1')+'_up')
    b = models.IntegerField(
        default=0, name=getattr(Eduuser, 'TYPE_2')+'_up')
    ...

    c = models.IntegerField(
        default=0, name=getattr(Eduuser, 'TYPE_1')+'_votes')
    d = models.IntegerField(
        default=0, name=getattr(Eduuser, 'TYPE_2')+'_votes')
    ...

    def perc(self):
        perc_array = []
        for user_type in getattr(Eduuser, 'USER_TYPE_CHOICES'):
            up = float(getattr(self, user_type[0]+'_up')) #Prevent int division
            votes = getattr(self, user_type[0]+'_votes')
            if votes==0:
                perc_array.append(0)
            else:
                perc_array.append(round(up/votes, 3))
        return perc_array

虽然我预计不会添加更多类型,但我希望代码看起来更干净。循环用户类型的最佳尝试是:

class Eduuser(AbstractUser):
    ...

class Vote(models.Model):
    for user_type in getattr(Eduuser, 'USER_TYPE_CHOICES'):
        models.IntegerField(
            default=0, name=user_type[0]+'_up')
        models.IntegerField(
            default=0, name=user_type[0]+'_votes')

    def perc(self):
        ...

然而,这不保存字段(我猜是因为缺少赋值运算符)。 所以有几个简单的问题:

1)有没有办法保存字段而不明确指定名称?或者我可以将字符串名称转换为变量(从我读过的其他帖子,这似乎是一个坏主意)?

2)我甚至在逻辑上接近这个投票的想法吗?我的一部分感觉就像有一种更容易的方法来跟踪多种类型用户的投票。

任何帮助表示赞赏!谢谢!

2 个答案:

答案 0 :(得分:1)

django-model-utils可以使用 Choices 帮助器使其变得更干净。

您可以通过以下方式执行Vote模型(未经测试):

from model_utils import Choices


class User(AbstractUser):
    USER_CHOICES = Choices(
        ('one', 'Type 1'),
        ('two', 'Type 2'),
    )

   user_type = models.CharField(max_length=10, choices=USER_CHOICES)


class Vote(models.Model):
    """
    A single vote on a `User`. Can be up or down.
    """
    VOTE_CHOICES = Choices(
        ('upvote'),
        ('downvote'),
    )

    user = models.ForeignKey(User)
    vote = models.CharField(max_length=10, choices=VOTE_CHOICES)

使用示例 - 获取所有“Type 1”用户的肯定投票数:

# retrieve all of the votes
all_votes = Vote.objects.all()
all_votes_count = len(all_votes)

# now retrieve all of the votes for users of ‘Type 1’    
type_one_votes = all_votes.filter(user__user_type=User.USER_CHOICES.one)
type_one_votes_count = len(type_one_votes)

# …and now filter the positive votes for ‘Type 1’ users
type_one_positives = type_one_votes.filter(vote=Vote.VOTE_CHOICES.upvote)
type_one_positive_vote_count = len(type_one_positives)

# etc.

答案 1 :(得分:0)

Django使用一些元类行为来根据你声明的内容创建字段,所以这并非完全无关紧要。您可以使用一些未记录的调用来动态地向模型类添加字段 - 请参阅以下文章:

http://blog.jupo.org/2011/11/10/django-model-field-injection/

那就是说,我会推荐一种更简单的方法。创建一个模型来保存可能的用户类型,然后将其用作投票表中的外键:

class UserType(models.Model):
    type_name = models.CharField()

class Vote(models.Model):
    user_type = models.ForeignKey(UserType)
    total = models.PositiveIntegerField()

或根据需要跟踪个人投票和总和,记录投票的用户或投票时的用户类型。如果用户在投票后更改了类,您可能需要保存用户的类型,这取决于您要执行的操作。

如果您只跟踪总和,则必须更仔细地考虑交易问题 - 我会说跟踪用户并强制执行唯一性约束。