deep-dive into django #1
TRANSCRIPT
from django.db import models
class User(models.Model): email = models.EmailField( max_length=254, unique=True, help_text='Used as the username')
from django.db import models
class User(models.Model): email = models.EmailField( max_length=254, unique=True, help_text='Used as the username')
name = models.CharField(max_length=50)
from django.db import models
class User(models.Model): email = models.EmailField( max_length=254, unique=True, help_text='Used as the username')
name = models.CharField(max_length=50) phone = models.CharField(max_length=20, null=True, blank=True)
from django.db import models
class User(models.Model): email = models.EmailField( max_length=254, unique=True, help_text='Used as the username')
name = models.CharField(max_length=50) phone = models.CharField(max_length=20, null=True, blank=True)
STATUS_PAID = 'paid' STATUS_TRIALING = 'trialing'
from django.db import models
class User(models.Model): email = models.EmailField( max_length=254, unique=True, help_text='Used as the username')
name = models.CharField(max_length=50) phone = models.CharField(max_length=20, null=True, blank=True)
STATUS_PAID = 'paid' STATUS_TRIALING = 'trialing'
STATUS_CHOICES = ( (STATUS_PAID, 'Paid'), (STATUS_TRIALING, 'Trialing'), )
from django.db import models
class User(models.Model): email = models.EmailField( max_length=254, unique=True, help_text='Used as the username')
name = models.CharField(max_length=50) phone = models.CharField(max_length=20, null=True, blank=True)
STATUS_PAID = 'paid' STATUS_TRIALING = 'trialing'
STATUS_CHOICES = ( (STATUS_PAID, 'Paid'), (STATUS_TRIALING, 'Trialing'), ) status = models.CharField( max_length=20, unique=True, choices=STATUS_CHOICES)
$ ./manage.py dbshellpsql (9.4.1)Type "help" for help.
vida=> \d accounts_user
Table "public.accounts_user" Column | Type | Modifiers--------+------------------------+---------------------- id | integer | not null default ... email | character varying(254) | not null name | character varying(50) | not null phone | character varying(20) | status | character varying(20) | not null
$ ./manage.py dbshellpsql (9.4.1)Type "help" for help.
vida=> \d messaging_message
Table "public.messaging_message" Column | Type | Modifiers-----------+---------+---------------------- id | integer | not null default ... sender_id | integer | not null
$ ./manage.py shell_plusIn [1]: user = ...
In [2]: user.message_set.all()Out[2]: [<Message: ...>, <Message: ...>]
$ ./manage.py shell_plusIn [1]: user = ...
In [2]: user.message_set.all()Out[2]: [<Message: ...>, <Message: ...>]
$ ./manage.py shell_plusIn [1]: user = ...
In [2]: user.messages_sent.all()Out[2]: [<Message: ...>, <Message: ...>]
ModelUser
ManagerUser.objects
QuerysetUser.objects.all()
User.objects.filter(...)
Python Listlist(User.objects
.all())
eval via DB
User.objects.all() # => querysetUser.objects.filter(email='...') # => querysetUser.objects.filter(email='...').count() # DB access
User.objects.filter(email='...')User.objects.filter(email='...', first_name='...')User.objects.filter(email='...').filter(first_name='...')
User.objects.filter(email='...')User.objects.filter(email='...', first_name='...')User.objects.filter(email='...').filter(first_name='...')
first call creates
QuerySet from Manager
User.objects.filter(email='...')User.objects.filter(email='...', first_name='...')User.objects.filter(email='...').filter(first_name='...')
second call transforms
QuerySet into another
QuerySet
first call creates
QuerySet from Manager
User.objects.filter(email='...').delete()User.objects.delete()User.objects.all().delete()
.delete() is only defined on QuerySet, not on Manager (for safety)
from django.db.models import Q
is_user_query = Q(email='...') | Q(first_name='...')User.objects.filter(is_user_query)
from django.db.models import Q
is_user_query = Q(email='...') | Q(first_name='...')User.objects.filter(is_user_query)
transform search criteria into
first-class value
users = Users.objects.filter(...)
for user in users: for tm in user.tracked_metrics.all(): print tm.metric.key
users = Users.objects.filter(...)
for user in users: for tm in user.tracked_metrics.all(): print tm.metric.key
Assume 10 users and 16 distinct metrics
users = Users.objects.filter(...)
for user in users: for tm in user.tracked_metrics.all(): print tm.metric.key
1 - users10 - tracked metrics16 - metrics
users = (Users.objects.filter(...) .prefetch_related('tracked_metrics'))
for user in users: for tm in user.tracked_metrics.all(): print tm.metric.key
1 - users 1 - tracked metrics16 - metrics
users = (Users.objects.filter(...) .prefetch_related('tracked_metrics__metric'))
for user in users: for tm in user.tracked_metrics.all(): print tm.metric.key
1 - users 1 - tracked metrics 1 - metrics
users = (User.objects .filter(customer_profile__isnull=False))
for user in users: print user.customer_profile.uuid
users = (User.objects .filter(customer_profile__isnull=False))
for user in users: print user.customer_profile.uuid
Assume 10 users
users = (User.objects .filter(customer_profile__isnull=False))
for user in users: print user.customer_profile.uuid
1 - users10 - customer profiles
users = (User.objects .filter(customer_profile__isnull=False) .prefetch_related('customer_profile'))
for user in users: print user.customer_profile.uuid
1 - users 1 - customer profiles
users = (User.objects .filter(customer_profile__isnull=False) .select_related('customer_profile'))
for user in users: print user.customer_profile.uuid
1 - users 0 - customer profiles using JOIN
$ ./manage.py makemigrations
current model
definition
model definition based on applied
migrations
DIFF
$ ./manage.py makemigrations$ ./manage.py migrate$ ./manage.py dbshellpsql (9.4.1)Type "help" for help.
vida=>
$ ./manage.py makemigrations$ ./manage.py migrate$ ./manage.py dbshellpsql (9.4.1)Type "help" for help.
vida=> SELECT * FROM django_migrations; id | app | name | applied----+--------------+--------------+-------------------------- 1 | accounts | 0001_initial | 2016-08-30 11:56:29.798795-07(1 row)
$ ./manage.py migrate accounts 0002 # to specific version$ ./manage.py migrate accounts 0001 # rollback
$ ./manage.py migrate accounts 0002 # to specific version$ ./manage.py migrate accounts 0001 # rollback$ ./manage.py migrate accounts 0002 --fake # only add to table
vida=> -- faking a migrationvida=> INSERT INTO django_migrations (app, name, applied) VALUES ('accounts', '0003_faked', current_timestamp);
class User(BaseModel): email = models.EmailField(max_length=254, unique=True)- phone_number = models.CharField(max_length=50)
class User(BaseModel): email = models.EmailField(max_length=254, unique=True)- phone_number = models.CharField(max_length=50)
+class Migration(migrations.Migration):+ operations = [+ migrations.RemoveField(+ model_name='user',+ name='phone_number',+ ),+ ]
class User(BaseModel): email = models.EmailField(max_length=254, unique=True)- phone_number = models.CharField(max_length=50)
+class Migration(migrations.Migration):+ operations = [+ migrations.RemoveField(+ model_name='user',+ name='phone_number',+ ),+ ]
class User(BaseModel): email = models.EmailField(max_length=254, unique=True)- phone_number = models.CharField(max_length=50)
+class Migration(migrations.Migration):+ operations = [+ migrations.RemoveField(+ model_name='user',+ name='phone_number',+ ),+ ]
Commit and deploy first.
class User(BaseModel): email = models.EmailField(max_length=254, unique=True)- phone_number = models.CharField(max_length=50)
+class Migration(migrations.Migration):+ operations = [+ migrations.RemoveField(+ model_name='user',+ name='phone_number',+ ),+ ]
Commit and deploy first.
Then commit and deploy.
Deploy code(model change only)
Run migrations(remove field)
2
3
User
User
Run migrations(None)
1
Deploy code(model change only)
Run migrations(remove field)
2
3
User
User
Run migrations(None)
1
Deploy code(None)
4
class User(BaseModel):- first_name = models.CharField(max_length=50)- first_name = models.CharField(max_length=50, db_index=True)
class User(BaseModel):- first_name = models.CharField(max_length=50)- first_name = models.CharField(max_length=50, db_index=True)
+class Migration(migrations.Migration):+ operations = [+ migrations.AlterField(+ model_name='user',+ name='first_name',+ field=models.CharField(max_length=50, db_index=True),+ ),+ ]
$ ./manage.py sqlmigrate accounts 0004BEGIN;CREATE INDEX "accounts_user_first_name_a03f23825d126a2_uniq" ON "accounts_user" ("first_name");
COMMIT;
$ ./manage.py sqlmigrate accounts 0004BEGIN;CREATE INDEX "accounts_user_first_name_a03f23825d126a2_uniq" ON "accounts_user" ("first_name");
COMMIT;
Locks the entire table!
CREATE INDEX CONCURRENTLY "accounts_user_first_name" ON "accounts_user" ("first_name");
Running this is appropriate.
$ aptible ssh --app vida-webserver$ ./manage.py dbshellpsql (9.4.1)Type "help" for help.
vida=> CREATE INDEX CONCURRENTLY "accounts_user_first_name" ON "accounts_user" ("first_name");CREATE_INDEX
$ aptible ssh --app vida-webserver$ ./manage.py dbshellpsql (9.4.1)Type "help" for help.
vida=> CREATE INDEX CONCURRENTLY "accounts_user_first_name" ON "accounts_user" ("first_name");CREATE_INDEX
vida=> \d accounts_user
$ aptible ssh --app vida-webserver$ ./manage.py dbshellpsql (9.4.1)Type "help" for help.
vida=> CREATE INDEX CONCURRENTLY "accounts_user_first_name" ON "accounts_user" ("first_name");CREATE_INDEX
vida=> \d accounts_user
vida=> INSERT INTO django_migrations (app, name, applied) VALUES ('accounts', '0004_auto_20160830_1124', current_timestamp);INSERT 0 1
$ aptible ssh --app vida-webserver$ ./manage.py dbshellpsql (9.4.1)Type "help" for help.
vida=> CREATE INDEX CONCURRENTLY "accounts_user_first_name" ON "accounts_user" ("first_name");CREATE_INDEX
vida=> \d accounts_user
vida=> INSERT INTO django_migrations (app, name, applied) VALUES ('accounts', '0004_auto_20160830_1124', current_timestamp);INSERT 0 1
vida=> SELECT * FROM django_migrations;
$ aptible ssh --app vida-webserver$ ./manage.py dbshellpsql (9.4.1)Type "help" for help.
vida=> CREATE INDEX CONCURRENTLY "accounts_user_first_name" ON "accounts_user" ("first_name");CREATE_INDEX
vida=> \d accounts_user
vida=> INSERT INTO django_migrations (app, name, applied) VALUES ('accounts', '0004_auto_20160830_1124', current_timestamp);INSERT 0 1
vida=> SELECT * FROM django_migrations; Now it’s safe to deploy.