queryset query queries python sql django django-models django-orm

python - query - IZQUIERDA ÚNETE a Django ORM



group by django (4)

Tengo los siguientes modelos:

class Volunteer(models.Model): first_name = models.CharField(max_length=50L) last_name = models.CharField(max_length=50L) email = models.CharField(max_length=50L) gender = models.CharField(max_length=1, choices=GENDER_CHOICES) class Department(models.Model): name = models.CharField(max_length=50L, unique=True) overseer = models.ForeignKey(Volunteer, blank=True, null=True) location = models.CharField(max_length=100L, null=True) class DepartmentVolunteer(models.Model): volunteer = models.ForeignKey(Volunteer) department = models.ForeignKey(Department) assistant = models.BooleanField(default=False) keyman = models.BooleanField(default=False) captain = models.BooleanField(default=False) location = models.CharField(max_length=100L, blank=True, null=True)

Quiero consultar para todos los departamentos que no tienen voluntarios asignados a ellos. Puedo hacerlo usando la siguiente consulta:

SELECT vsp_department.name FROM vsp_department LEFT JOIN vsp_departmentvolunteer ON vsp_department.id = vsp_departmentvolunteer.department_id WHERE vsp_departmentvolunteer.department_id IS NULL;

¿Hay una manera más similar a django de hacer esto o debería ir con raw sql?


Esto parece estar funcionando:

Department.objects.filter(departmentvolunteer__department__isnull=True)

Ver documentos para más detalles.


Puede hacer esto siguiendo la relación hacia atrás en la búsqueda.

>>> qs = Department.objects.filter(departmentvolunteer__isnull=True).values_list(''name'', flat=True) >>> print(qs.query) SELECT "app_department"."name" FROM "app_department" LEFT OUTER JOIN "app_departmentvolunteer" ON ( "app_department"."id" = "app_departmentvolunteer"."department_id" ) WHERE "app_epartmentvolunteer"."id" IS NULL

Aquí están los documentos sobre consultas "que abarcan relaciones multivaluadas": https://docs.djangoproject.com/en/stable/topics/db/queries/#spanning-multi-valued-relationships


Para mí fueron necesarios modelos de combinación personalizada, que tienen campos implícitos
me funciona en django 1.9.
pero parece más en la muleta
Si alguien tiene una solución más elegante, por favor comparte para las personas

from django.db.models.sql.datastructures import Join from django.db.models.fields.related import ForeignObject from django.db.models.options import Options from myapp.models import Ace from myapp.models import Subject jf = ForeignObject( to=Subject, on_delete=lambda: x, from_fields=[None], to_fields=[None], rel=None, related_name=None ) jf.opts = Options(Ace._meta) jf.opts.model = Ace jf.get_joining_columns = lambda: (("subj", "name"),) j=Join( Subject._meta.db_table, Ace._meta.db_table, ''T1'', "LEFT JOIN", jf, True) q=Ace.objects.filter(version=296) q.query.join(j) print q.query

resultado:

SELECT `ace`.`id`, `ace`.`version_id`, `ace`.`obj`, `ace`.`subj`, `ace`.`ACE_Type`, `ace`.`ACE_Inheritance`, `ace`.`ACE_Rights` FROM `ace` LEFT OUTER JOIN `core_subject` ON (`ace`.`subj` = `core_subject`.`name`) WHERE `ace`.`version_id` = 296

aquí ejemplo de uso con condición adicional y establecer alias de tabla (pero parece muleta)

def join_to(self, table1, table2, field1, field2, queryset, alias=''''): """ table1 base """ # here you can set complex clause for join def extra_join_cond(where_class, alias, related_alias): if (alias, related_alias) == (''[sys].[columns]'', ''[sys].[database_permissions]''): where = ''[sys].[columns].[column_id] = '' / ''[sys].[database_permissions].[minor_id]'' children = [ExtraWhere([where], ())] wh = where_class(children) return wh return None dpj = ForeignObject( to=table2, on_delete=lambda: None, from_fields=[None], to_fields=[None], rel=None, related_name=None ) dpj.opts = Options(table1._meta) dpj.opts.model = table1 dpj.get_joining_columns = lambda: ((field1, field2),) dpj.get_extra_restriction = extra_join_cond dj = Join( table2._meta.db_table, table1._meta.db_table, ''T'', "LEFT JOIN", dpj, True) ac = queryset._clone() ac.query.join(dj) # hook for set alias alias and setattr(dj, ''table_alias'', alias) return ac

Lo uso por

# how it use: from django.db.models.expressions import Col q = Something.objects / .filter(type__in=["''S''", "''U''", "''G''"]) / .exclude(name__in=("''sys''", "''INFORMATION_SCHEMA''")) / .annotate( ... some annotation fields class_= Col(Permissions._meta.db_table, Permissions._meta.get_field(''field_name''), output_field=IntegerField()), Grant=Col( ''T10'', Principals._meta.get_field(''name''), output_field=CharField()), ).values(''Grant'') ac = self.join_to(Principals, ServerPrincipals, ''sid'', ''sid'', q) # here invoke "extra_join_cond" of function "join_to" ac = self.join_to(Permissions, Columns, ''major_id'', ''object_id'', ac) # here use alias table ac = self.join_to(Permissions, Principals, ''grantor_id'', ''principal_id'', ac, ''T10'') # T10 is alias

sql''ll ser

SELECT T10.name AS Grant FROM sys.principals LEFT OUTER JOIN sys.server_principals ON (sys.principals.sid = sys.server_principals.sid) LEFT OUTER JOIN sys.columns ON (sys.permissions.major_id = sys.columns.object_id AND ( (sys.columns.column_id = sys.permissions.minor_id)) ) LEFT OUTER JOIN sys.principals T10 ON (sys.permissions.grantor_id = T10.principal_id)


para crear fácilmente unirse por OR

def get_queryset(self): qs = super(AceViewSet, self).get_queryset() qs = qs.select_related(''xxx'') # construct all tables and the join dependence qs.query.__str__() qs.query.alias_map[''xx_subject''].join_cols = ((''xxx_id'', ''uid''), (''xxx_id'', ''ad_subject_id'')) qs.query.alias_map[''xx_subject''].as_sql = partial(self.as_sql, qs.query.alias_map[''xx_subject'']) return qs @staticmethod def as_sql(self, compiler, connection): sql, params = Join.as_sql(self, compiler, connection) or_sql = sql.replace("AND", "OR") return or_sql, params

FROM "xx_ace" LEFT OUTER JOIN "xx_subject" ON ("xx_ace"."xxx_id" = "xx_subject"."uid" OR "xx_ace"."xxx_id" = "xx_subject"."ad_subject_id")