我有三种型号 Learner ,等级和直通模型 LearnerLevel 。
模型如下:
class Learner(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
words = models.ManyToManyField('Word', through='LearnerWord', related_name='Learners')
levels = models.ManyToManyField('Level', through='LearnerLevel', related_name='Learners')
def __str__(self):
return self.user.username
class Level(models.Model):
name = models.CharField(max_length=100)
selectedByUser = models.BooleanField(default=False)
words = models.ManyToManyField('Word', through='LevelWord', related_name='levels')
countWords = models.IntegerField(default=0)
language = models.ForeignKey('Language', related_name='languagelevels', on_delete=models.CASCADE, null=True,
blank=True)
def __str__(self):
return self.name
class LearnerLevel(models.Model):
learner = models.ForeignKey('Learner', related_name='learnerlevels', on_delete=models.SET_NULL, null=True)
level = models.ForeignKey('Level', related_name='learnerlevels', on_delete=models.SET_NULL, null=True, blank=True)
knownWords = models.IntegerField(default=0)
def __str__(self):
return self.learner.user.username + ' ' + self.level.name
表格如下:
SELECT * FROM app_level;
id|name |countWords|language_id|selectedByUser
1 |A1 |0 |1 |0
2 |A1 Supermarkt |0 |1 |0
3 |A1 |22 |2 |0
4 |A1 Albanisch1 |20 |4 |0
5 |A1 Albanisch2 |17 |4 |0
6 |A1 Albanisch3 |21 |4 |0
7 |A1 Albanisch4 |20 |4 |0
8 |A1 Albanisch4 |20 |4 |0
9 |A1 Albanisch5 |20 | |0
10|A1 Albanisch7 |20 |4 |0
11|A1 Albanisch8 |20 |4 |0
12|A1 Albanisch9 |20 |4 |0
13|A1 Albanisch10|20 |4 |0
14|A1 Albanisch11|20 |4 |0
15|A1 Albanisch12|20 |4 |0
SELECT * FROM wordapp_learnerlevel;
id|knownWords|learner_id|level_id
1 |8 |1 |4
2 |16 |1 |5
3 |3 |1 |6
4 |8 |1 |1
5 |8 |2 |4
我的第一个问题是,如何使用Django实现以下目标?
SELECT wordapp_level.name,
wordapp_level.countWords,
wordapp_learnerlevel.knownWords
FROM wordapp_level
INNER JOIN wordapp_learnerlevel
ON wordapp_level.id = wordapp_learnerlevel.level_id;
name |countWords|knownWords
A1 Albanisch1|20 |8
A1 Albanisch2|17 |16
A1 Albanisch3|21 |3
A1 |0 |8
A1 Albanisch1|20 |8
我的目标是从django-rest-framework中获得一个可以在get_queryset函数中提供给modelview的新查询集。 我的第二个问题是,这是好习惯吗?
我想要这样的东西:
def get_queryset(self):
learnerStatistic = LearnerLevel.objects.select_related('level')
return learnerStatistic
因此,例如,学习者已从id为1 2 3的级别学习。现在在learningerlevel中,更改了namedWords的level_id为1 2 3的单词。现在,我想为用户提供一个统计信息。 “您从 level.name 学到了 level.countWords 个 learnerlevel.knownWords 。”
从Django文档中我不清楚
==========编辑=========
我尝试过
str(LearnerLevel.objects.select_related('level').query)
我明白了
SELECT "wordapp_learnerlevel"."id", "wordapp_learnerlevel"."learner_id", "wordapp_learnerlevel"."level_id", "wordapp_learnerlevel"."knownWords", "wordapp_level"."id", "wordapp_level"."name", "wordapp_level"."selectedByUser", "wordapp_level"."countWords", "wordapp_level"."language_id" FROM "wordapp_learnerlevel" LEFT OUTER JOIN "wordapp_level" ON ("wordapp_learnerlevel"."level_id" = "wordapp_level"."id")'
但何时
str(LearnerLevel.objects.select_related('level')。values()。query)
我明白了
SELECT "wordapp_learnerlevel"."id", "wordapp_learnerlevel"."learner_id", "wordapp_learnerlevel"."level_id", "wordapp_learnerlevel"."knownWords" FROM "wordapp_learnerlevel"
但是我只想要 learnerlevel.knownWords , level.countWords , level.name
如果我愿意
qs = LearnerLevel.objects.select_related('level').only('knownWords','level__name','level__countWords')
然后
qs.values()
是
<QuerySet [{'id': 1, 'learner_id': 1, 'level_id': 4, 'knownWords': 8}, {'id': 2, 'learner_id': 1, 'level_id': 5, 'knownWords': 16}, {'id': 3, 'learner_id': 1, 'level_id': 6, 'knownWords': 3}, {'id': 4, 'learner_id': 1, 'level_id': 1, 'knownWords': 8}, {'id': 5, 'learner_id': 2, 'level_id': 4, 'knownWords': 8}]>
但我希望是
<QuerySet [{'id': 1, 'name': 'A1', 'knownWords': 8, 'countWords': xx}, .....]>
=======解决方案======
我找到了解决方法
def get_queryset(self):
currLearner = Learner.objects.get(user=self.request.user)
return currLearner.learnerlevels.values('knownWords', 'level__name', 'level__countWords')
而序列化器是
class LearnerLevelXXSerializer(serializers.Serializer):
knownWords = serializers.IntegerField()
level__name = serializers.CharField(max_length=200)
level__countWords = serializers.IntegerField()
谢谢Denis Cornehl。您的建议对我有帮助
答案 0 :(得分:1)
关于第一个问题:
您可以通过看到的queryset精确地获得结果。由于该关系为1:n(一个LearnerLevel
可以有多个Level
),因此您从LearnerLevel
和select_related
Level
对象开始。
总是能帮助我从纯SQL到Django ORM的一件事:
使用.query
并转换为字符串,然后您将看到django生成的sql查询(已添加sql格式):
./manage.py shell
>>> from wordapp.models import Level, LearnerLevel
>>> qs = LearnerLevel.objects.select_related('level')
>>> str(qs.query)
'SELECT
"wordapp_learnerlevel"."id",
"wordapp_learnerlevel"."learner_id",
"wordapp_learnerlevel"."level_id",
"wordapp_learnerlevel"."knownWords",
"wordapp_level"."id",
"wordapp_level"."name",
"wordapp_level"."selectedByUser",
"wordapp_level"."countWords"
FROM "wordapp_learnerlevel"
LEFT OUTER JOIN "wordapp_level" ON (
"wordapp_learnerlevel"."level_id" = "wordapp_level"."id"
)'
>>>
现在,您当然可以减少结果列(通过使用.values()
,.only()
或类似方法),通常,尤其是当您使用其他django库(例如django-rest-framework)时,它更容易直接使用模型实例,直到您必须优化性能。
关于第二个问题: 这可能是个好习惯。通常,对于REST-API,您会尝试考虑资源和资源列表(DRF在顶部添加了 list / detail action )。在我看来,您的示例适合资源,因为您将返回所有用户语言的级别。
希望我能帮上忙。