django-signals vs triggers?
Asked Answered
C

4

16

I read about django signals (http://docs.djangoproject.com/en/dev/topics/signals/), but as far as I understand, signals are never converted into literal SQL triggers (http://en.wikipedia.org/wiki/Database_trigger).

If I'm correct that signals and triggers are different, then which one is better and in what ways? What's the best practice?

....................

Here's a concrete example if you want one:

class Location(models.Model):
    name = models.CharField(max_length=30)

class Person(models.Model):
    location = models.ForeignKey('Location')

class Team(models.Model):
    locations = models.ManyToManyField('Location')

I want a person to be able to join a team if and only if that person's location is within that team's set of locations. I do not know how to do that with normal relational constraints, so as far as I know I'm forced to use triggers or signals. My gut says that I should use triggers but I want to know best practice.

Callaghan answered 21/8, 2010 at 21:52 Comment(0)
O
20

Neither. The best tool for this job is model validation - you can write your custom validation rule there and it will be enforced in the admin and your own apps.

Onofredo answered 21/8, 2010 at 22:3 Comment(6)
+1: That and a simple override to save in the model covers all the bases I've ever encountered.Northwest
+1. Signals usually slow your tests down if you are loading fixtures that trigger signals. It is a pain to work around by disconnect ing before tests and connect ing afterwards.Cowboy
I have two goals: 1) make the site do what I want (validate) 2) catch me when I make a mistake. In terms of #1, this suggestion makes sense. In terms of #2, what if I don't use a ModelForm to interact with the DB? the documentation says "Note that validators will not be run automatically when you save a model". That means I now can accidentally lose data integrity since I didn't invoke a validator by calling Person.save() before modifying the DB. But with triggers, it's impossible for the trigger to be mistakingly circumvented. Does my reasoning make sense?Callaghan
Well, up to a certain point. Your problem will be in reporting to the user that the constraint has operated - you'll need to wrap every ORM call in a try/except block to avoid an unhandled db exception leading to a 500 page, at which point you may as well have used Django's own methods anyway.Onofredo
As S.Lott mentioned, you could add validation rules and override .save() to call .full_clean() to ensure your data is checked on every save. This might break locations that save and don't gracefully handle validation errors, but that might be preferable to saving invalid data. You're right that a trigger would be harder to bypass. It would also be faster, since it would be running inside the database. The downside is it might be more difficult to maintain, as you'd have to implement all your validation rules in SQL.Occupy
How come this is even a solution? It is clearly mentioned that validators are run only in conjunction with ModelForm. docs.djangoproject.com/en/dev/ref/validators/…Jacobine
R
14

Django signals are awesome (validation is awesome too, but sometimes you need to change something before save…). If you are working with database ONLY thru Django, it's really good idea to keep all logic in same place, imho.

Here is an example, how it works:

class Example(models.Model):
    ''' Example of Model (I hate foo-bars!) '''
    age = models.IntegerField()
    can_buy_beer = models.BooleanField(default=False)


def set_can_buy_beer(sender, instance, **kwargs):
    ''' Trigger body '''
    if instance.age >= 21:
        instance.can_buy_beer = True
    else:
        instance.can_buy_beer = False

# ↓ Magic — now, field Example.can_buy_beer will be autocalculated on each save!
pre_save.connect(set_can_buy_beer, sender=Example) 
Roofdeck answered 25/1, 2012 at 13:27 Comment(1)
I prefer this solution to model validation since model validation only works with ModelForms docs.djangoproject.com/en/dev/ref/validators/…Jacobine
F
5

Main advantages of Triggers over Signals:

  • independent of the application: makes the migration to a new frameworks/languages easier (since triggers and, in some cases, stored procedure are dump with your DB)

  • safety: depending on the situation, you could restrict the UPDATE rights on some tables and still be able to run your app (think of critical history or transaction tables, who knows which exploits might be discovered in the next 10 years)

  • reduce the number of requests your app have to address to the DBMS (especially useful in the context of a distributed architecture).

Here are the main advantages. The main cons is that you have to deal with the old school SQL syntax.

Flagellant answered 14/12, 2018 at 20:35 Comment(0)
M
4

You can use triggers to enforce this kind of constraints, but I wouldn't rely on that. This can only be done as a secondary enforcement, while the primary one is to be model validation, just as Daniel already said.

As for DB triggers vs Django signals they are more different the common. The only common thing they share is that both are invoked upon entity change. But the entities differ very much.

Triggers monitor database row changes, thus they operate on raw tabular data. Trigger code is run by DBMS.

In contrast to triggers signals monitor domain object changes. In a generic case Django's model consists of data from several table rows (consider model inheritance and related object subsets). Signal code is run by Django.

Martelli answered 21/8, 2010 at 22:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.