内部联接两个查询集

时间:2019-11-30 19:28:09

标签: python sql django sqlite django-rest-framework

我有三种型号 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。您的建议对我有帮助

1 个答案:

答案 0 :(得分:1)

关于第一个问题: 您可以通过看到的queryset精确地获得结果。由于该关系为1:n(一个LearnerLevel可以有多个Level),因此您从LearnerLevelselect_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 )。在我看来,您的示例适合资源,因为您将返回所有用户语言的级别。

希望我能帮上忙。