Package: django-raw-sugar
django-raw-sugarTurns your raw sql into a QuerySet.InstallationInstall usingpip...pip install django-raw-sugarHow to useBasic usageAttachRawManagerinstance to your model. Then use it's.from_raw()method.RawManager.from_raw(raw_query=None, params=None, translations=None, null_fields=None, db_table=None)You should provide eitherraw_queryordb_table(but not both).# models.pyfromdjango.dbimportmodelsfromraw_sugarimportRawManagerclassMySimpleModel(models.Model):name=models.TextField()number=models.IntegerField()source=models.ForeignKey(AnotherSimpleModel,models.DO_NOTHING)objects=RawManager()# some other filefrom.modelsimportMySimpleModelqueryset=MySimpleModel.objects.from_raw('SELECT Null as id, "my str" as name, 111 as number, Null as source_id')The result of your raw sql must contain all the fields that are present in target model, including primary key and foreign keys. If you know your raw sql lacks some fields, you can provide thenull_fieldsargument instead of modifying your query:queryset=MySimpleModel.objects.from_raw('SELECT "my str" as name, 111 as number',null_fields=['id','source_id'])The resulting queryset is a regularmodels.QuerySetinstance, and can be handled accordingly:queryset=queryset.filter(number__gte=10)\.exclude(number__gte=1000)\.filter(name__contains='s')\.order_by('number')\.select_related('source')print(queryset[0].name)# "my str"Passing parametersIf you need to perform parameterized queries, you can use theparamsargument:queryset=MySimpleModel.objects.from_raw('SELECT "%s" as name, 111 as number',params=['my str'],null_fields=['id','source_id'])If you want to pass params deferred, you can use thewith_paramsmethod:queryset=MySimpleModel.objects.from_raw('SELECT "%s" as name, 111 as number',null_fields=['id','source_id'])queryset=queryset.with_params('my str')Using transtalionsIf the field names of queried table differ from the model field names, you can map fields by using thetranslationsargument:queryset=MySimpleModel.objects.from_raw('SELECT "%s" as name, 111 as inner_number',params=['my str'],translations={'inner_number':'number'},null_fields=['id','source_id'])Pre defined source raw sqlYou can define a model manager that uses your raw sql as query source by default. You can do this by passing afrom_rawargument to RawManager, or by using theraw_managerdecorator to method that returns aFromRawinstance:fromdjango.dbimportmodelsfromraw_sugarimportraw_manager,RawManager,FromRawclassMySimpleModel(models.model):name=models.TextField()number=models.IntegerField()source=models.ForeignKey(AnotherSimpleModel,models.DO_NOTHING)my_raw_manager=RawManager(FromRaw('SELECT "my str" as name, 111 as number',null_fields=['id','source_id']))@raw_managerdefmy_raw_manager_2(cls):returnFromRaw('SELECT "my str" as name, 111 as number',null_fields=['id','source_id'])@raw_manager(is_callable=True)defmy_callable_raw_manager(cls,name=""):returnFromRaw('SELECT%sas name, 111 as number',null_fields=['id','source_id'],params=[name])# some other filefrom.modelsimportMySimpleModelqueryset=MySimpleModel.my_raw_source.all()queryset=MySimpleModel.my_raw_source_2.all()queryset=MySimpleModel.my_callable_raw_source('my str').all()print(queryset[0].name)# "my str"TheFromRawclass accepts all the arguments as theRawManager.from_raw:
FromRaw(raw_query=None, params=None, translations=None, null_fields=None, db_table=None)When you use theraw_managerdecorator, the parameters you pass towith_paramsmethod will be passed into the decorated method, not into your raw. If you need this behavour, you can do it manually:@raw_manager(is_callable=True)defmy_callable_raw_manager(cls,*args):assertlen(args)==2returnFromRaw('SELECT%sas name,%sas number',null_fields=['id','source_id'],params=args)Querying views / table functionsIf you have a sql view or a sql table function in your database and want to query it, instead of passing sql likeSELECT * from my_viewyou can use thedb_tableargument:queryset=MySimpleModel.objects.from_raw(db_table='my_view')queryset=MySimpleModel.objects.from_raw(db_table='my_func(%s,%s)',params=['param',1])queryset=MySimpleModel.objects.from_raw(db_table='my_func(%s,%s)').with_params('param',1)Use a QuerySet as a sourceYou can use a QuerySet instance as a source instead of raw sql by returning aFromQuerySetinstance from decorated manager method:classMySimpleModel(models.Model):name=models.TextField()number=models.IntegerField()source=models.ForeignKey(AnotherSimpleModel,models.DO_NOTHING,null=True)@raw_managerdefmy_qs_manager(cls):returnFromQuerySet(cls.objects.values('source')\.annotate(_number=models.Sum('number')),translations={'_number':'number'})TheFromQuerySetclass accepts only a QuerySet and translations:FromQuerySet(queryset, translations=None)If the provided QuerySet lacks some fields, theNullwill be returned. You don't need to specifynull_fieldsas you would with theFromRaw.Differences withManager.raw()Pros:The result of executing of your raw sql is aQuerySet(!!!), and can filter, order, annotate, union, etc. it.Cons:The result of yourFromRawmust contain all fields of target model, including primary and foreign keys. If you omit any, you get anOperationalError('no such column: ...')exception.If you don't provide some fields in source QuerySet when use theFromQuerySet, this fields are filled withNull, and can not be loaded on demand. The Django'sRawQuerySetallows it.