How to create a model from a select query/CTE in peewee? - python

Lets say I have a table called flights and I want an aggregation on it like:
SELECT plane, count(flightId) as num from flights;
And let´s say I don´t want to create a permanent View.
Can I use an SQL Query or its results as the source for a Model?

No, you would need to write that as a view. You can likely create a temporary view if you don't want it to persist in your schema, and then define a model that uses your temp view.
class FlightCount(Model):
plane = TextField()
num = IntegerField()
class Meta:
table_name = 'flight_count'
#classmethod
def create_view(cls):
db.execute_sql('create temporary view flight_count as ...')
Then when you wish to use it,
FlightCount.create_view()
query = FlightCount.select(...)

Related

Django delete function overrides pgtrigger

I have created several models using Django, for one of the models I would like to have a trigger that does not allow deletes. See the model code below.
#pgtrigger.register(
pgtrigger.Protect(name='protect_deletes',operation=pgtrigger.Delete)
)
class NoDelete(models.Model):
salary = models.PositiveIntegerField()
id = models.PositiveIntegerField(primary_key=True)
name = models.CharField(max_length=50)
The application also has an website that allows you to interact with the database, when deleting a model of this type a function in views.py is called.
def delete(request, id):
temp = NoDelete.objects.get(pk=id)
temp.delete()
So it seems like the trigger does not function as I want it to since this delete removes the row from the database. When trying to delete the tuple directly from the database however the trigger is fired. Any ideas on how to get around this issue? I have to use a trigger in this assignment.

How do I retrieve a field from a Many-To-Many table?

I need to retrieve a value from a Many-To-Many query. Let's say I have 3 models: Toy, Part, and ToyParts
ToyParts has a field called "part_no". I need to be able to get the value of this.
class Toy(models.Model):
parts = models.ManyToManyField(Part, through="ToyParts")
class Part(models.Model):
pass
class ToyParts(models.Model):
toy = models.ForeignKey(Toy, ...)
part = models.ForeignKey(Part, ...)
part_no = models.CharField(...)
I've tried using:
toy.parts.all().first().part_no
which obviously doesn't work as Part does not have a field called "part_no"
I've also tried just simply using:
ToyParts.objects.filter(toy=..., part=...)
but that adds additional queries.
How would I be able to get part_no without querying ToyParts directly?
I've tried using: toy.parts.all().first().part_no
The part_no field is declared on the model ToyParts. You therefore need to get an instance of ToyParts to access this field. Assuming you have a Toy instance you can use the reverse relation to ToyParts, which defaults to toyparts_set, as follows:
toy.toyparts_set.first().part_no
How would I be able to get part_no without querying ToyParts directly?
You can't. If you want to reduce the number of queries you can use select_related:
for tp in toy.toyparts_set.select_related('part').all():
print(tp.part_no, tp.part.id)
In this example tp.part doesn't require an extra query as the part instance is already fetched by select_related.

Selecting only active records django query

In my Django project, I have an is_active boolean column in every table of my database. Every time I or the framework accesses the database, I want only the active records to show up. What is the standard way to achieve this? Certainly I don't want to check for is_active in every queries I make.
The easiest way to do this is to create a custom model manager, like this:
class OnlyActiveManager(models.Manager):
def get_queryset(self):
return super(OnlyActiveManager, self).get_queryset().filter(is_active=True)
Then, add it to your models:
class MyModel(models.Model):
objects = models.Manager()
active = OnlyActiveManager()
Next, use it like this:
foo = MyModel.active.all()
You can also use it to replace the default manager (called objects), but then you'll have to do custom queries to get all records that are in-active.
You can write a manager class for your model, A sample model manager is given below, for more you can refer Django official website
class MediaManager(models.Manager):
def get_queryset(self):
return MediaQuerySet(self.model, using=self._db)
def active(self):
return self.filter(is_active=True)
class Media(models.Model):
(..model fields..)
objects = MediaManager()
The query should be like
media = Media.objects.active()
you can do it by using django model managers.
please check django documentaion for detail django documentaion

Perform a SQL JOIN on Django models that are not related?

I have 2 Models, User (django.contrib.auth.models.User) and a model named Log. Both contain an "email" field. Log does not have a ForeignKey pointing to the User model. I'm trying to figure out how I can perform a JOIN on these two tables using the email field as the commonality.
There are basically 2 queries I want to be able to perform. A basic join for filtering
#Get all the User objects that have related Log objects with the level parameter set to 3.
User.objects.filter(log__level=3)
I'd also like to do some aggregates.
User.objects.all().anotate(Count('log'))
Of course, it would be nice to be able to do the reverse as well.
log = Log.objects.get(pk=3)
log.user...
Is there a way to do this with the ORM? Maybe something I can add to the model's Meta class to "activate" the relation?
Thanks!
You can add an extra method onto the User class, using MonkeyPatching/DuckPunching:
def logs(user):
return Log.objects.filter(email=user.email)
from django.contrib.auth.models import User
User.logs = property(logs)
Now, you can query a User, and ask for the logs attached (for instance, in a view):
user = request.user
logs = user.logs
This type of process is common in the Ruby world, but seems to be frowned upon in Python.
(I came across the DuckPunching term the other day. It is based on Duck Typing, where we don't care what class something is: if it quacks like a duck, it is a duck as far as we are concerned. If it doesn't quack when you punch it, keep punching until it quacks).
why not use extra()?
example (untested):
User.objects.extra(
select={
'log_count': 'SELECT COUNT(*) FROM myapp_log WHERE myapp_log.email = auth_user.email'
},
)
for the User.objects.filter(log__level=3) portion here is the equivalent with extra (untested):
User.objects.extra(
select={
'log_level_3_count': 'SELECT COUNT(*) FROM myapp_log WHERE (myapp_log.email = auth_user.email) AND (myapp_log.level=3)'
},
).filter(log_level_3_count__gt=0)
Do the Log.email values always correspond to a User? If so, how about just adding a ForeignKey(User) to the Log object?
class Log(models.Model):
# ...
user = models.ForeignKey(User)
With the FK to User, it becomes fairly straight forward to find what you want:
User.objects.filter(log__level=3)
User.objects.all().anotate(Count('log'))
user.log_set.all()
user.log_set.count()
log.user
If the Log.email value does not have to belong to a user you can try adding a method to a model manager.
class LogManager(models.Manager):
def for_user(self, user):
return super(LobManager, self).get_query_set().filter(email=user.email)
class Log(models.Model):
# ...
objects = LogManager()
And then use it like this:
user = User.objects.get(pk=1)
logs_for_user = Log.objects.for_user(user)

Database query across django ManyToManyField

I'd like to find how to select all objects whose ManyToMany field contains another object. I have the following models (stripped down)
class Category(models.Model):
pass
class Picture(models.Model):
categories = models.ManyToManyField(Category)
visible = models.BooleanField()
I need a function to select all the Pictures in one or more Categories:
def pics_in_cats(cat_ids=()):
pass
BUT it needs to return a QuerySet if possible so that I can do something like:
pics_in_cats((1,2,3)).filter(visible=True)
It could be done by loading all the relevant Category objects and merging their picture_set attributes, but that seems inefficient. I'd also like to avoid falling back to raw SQL if possible.
Thanks in advance
Why write a custom function and not use something like this? (untested)
pics = Picture.objects.filter(categories__in = [1,2,3]).filter(visible=True)

Categories