Slide 1

Slide 1 text

Paulo Alvarado @czpython SPEEDING UP

Slide 2

Slide 2 text

Hi, I’m Paulo Alvarado ✴ django CMS lead developer ✴ Web developer at ✴ Enjoy hiking and mountains

Slide 3

Slide 3 text

Speed is an essential factor

Slide 4

Slide 4 text

Topics ✴ ORM ✴ Templates ✴ Third Party Apps

Slide 5

Slide 5 text

Example Model class Hotel(models.Model):
 name = models.CharField(max_length=200)
 city = models.CharField(max_length=120)
 postcode = models.PositiveIntegerField()
 manager = models.ForeignKey( to='auth.User', blank=True, null=True ) modified_on = models.DateTimeField(auto_now=True)
 
 def __str__(self):
 return self.name

Slide 6

Slide 6 text

django ORM ✴ Prefetching ✴ Bulk create ✴ Using _id on foreign key fields ✴ Saving / updating ✴ Fetching only what you need

Slide 7

Slide 7 text

Prefetching ORM

Slide 8

Slide 8 text

Prefetching ORM In [1]: Hotel.objects.select_related(‘manager’) Out[1]: ... In [2]: Hotel.objects.prefetch_related(‘photos’) Out[2]: ...

Slide 9

Slide 9 text

Bulk Create ORM

Slide 10

Slide 10 text

Bulk Create ORM # Ran test create_v1 in 6.549 seconds # Ran test create_v2 in 0.477 seconds def create_v1(hotels): for hotel in hotels: hotel.save() def create_v2(hotels): Hotel.objects.bulk_create(hotels)

Slide 11

Slide 11 text

Using _id on Foreign Key fields

Slide 12

Slide 12 text

Using _id on Foreign Key fields ORM def has_manager(hotel): # Illustrates the usage of _id to avoid # a new query return bool(hotel.manager_id)

Slide 13

Slide 13 text

Saving / Updating ORM

Slide 14

Slide 14 text

Saving / Updating ORM def set_new_manager_v1(hotel, manager): hotel.manager = manager hotel.save() def set_new_manager_v2(hotel, manager): # Call save(update_fields) when you have full control # over the fields that change. hotel.manager = manager hotel.save(update_fields=['manager'])

Slide 15

Slide 15 text

Saving / Updating ORM def set_new_manager_v3(hotel, manager): # Call queryset.update() to avoid triggering # save signals. It will only run an SQL UPDATE. Hotel.objects.filter(pk=hotel.pk).update(manager=manager)

Slide 16

Slide 16 text

Saving / Updating ORM def set_new_manager_v4(lookup, hotel, manager): # Call queryset.update() to avoid triggering # save signals. It will only run an SQL UPDATE. lookup(pk=hotel.pk).update(manager=manager) lookup = Hotel.objects.filter for hotel, manager in hotels_and_managers: set_new_manager_v4(lookup, hotel, manager)

Slide 17

Slide 17 text

Saving / Updating ORM def update_manager_data(manager, **new_data): for field, value in new_data.items(): setattr(manager, field, value) # Not recommended as manager points to a third- # party model which might override the data # that gets written # manager.save(update_fields=list(new_data.keys())) manager.save()

Slide 18

Slide 18 text

Fetching only what you need ORM

Slide 19

Slide 19 text

Fetching only what you need ORM # Ran test print_hotel_names_v1 in 0.175117969513
 # Ran test print_hotel_names_v2 in 0.173953056335 def print_hotel_names_v1():
 for hotel in Hotel.objects.all():
 print hotel.name
 
 def print_hotel_names_v2():
 for hotel in Hotel.objects.only('name'):
 print hotel.name

Slide 20

Slide 20 text

Fetching only what you need ORM # Ran test print_hotel_names_v3 in 0.075676202774
 # Ran test print_hotel_names_v4 in 0.0628550052643 def print_hotel_names_v3():
 for data in Hotel.objects.values('name'):
 print data['name']
 
 def print_hotel_names_v4():
 for name in Hotel.objects.values_list('name', flat=True):
 print name

Slide 21

Slide 21 text

Fetching only what you need ORM 
 
 with open('hotels.csv', 'wb') as testfile:
 queryset = Hotel.objects.values_list( 'pk', 'name', 'city', ‘postcode' )
 csv_writer = csv.writer(testfile)
 csv_writer.writerow(['ID', 'Name', 'City', 'Post Code'])
 csv_writer.writerows(queryset.iterator())

Slide 22

Slide 22 text

Templates ✴ Cached template loader (projects only) ✴ Per request cached templates (reusable apps) ✴ Fragment caching ✴ More tags less template logic

Slide 23

Slide 23 text

Cached template loader Templates TEMPLATES = [{
 ... 
 'OPTIONS': {
 'loaders': [
 ('django.template.loaders.cached.Loader', [
 'django.template.loaders.filesystem.Loader',
 'django.template.loaders.app_directories.Loader',
 ]),
 ],
 }, 
 }]

Slide 24

Slide 24 text

Per request cached templates Templates 


Hotel List


 {% for hotel in hotels %}
 {% include "hotels/includes/hotel_info.html" %}
 {% endfor %}


Slide 25

Slide 25 text

Per request cached templates Templates

Hotel List With Cached Template


 {% for hotel in hotels %}
 {% include hotel_templates.hotel_info %}
 {% endfor %}


Slide 26

Slide 26 text

Fragment caching Templates 
 {% load cache %}

Hotel List With Fragment Caching


 {# 10 minute TTL #}
 {% cache 600 "hotel_list" hotels|length %}


 {% for hotel in hotels %}
 {% include hotel_templates.hotel_info %}
 {% endfor %}


 {% endcache %}

Slide 27

Slide 27 text

Fragment caching Templates 
 {% load cache %}

Hotel List Fragment Cache Russian Doll


 {# 10 minute TTL #}
 {% cache 600 "hotel_list" hotels|length %}


 {% for hotel in hotels %}
 {# 1 day TTL #}
 {% cache 86400 "hotel_list" hotel.pk hotel.modified_on %}
 {% include hotel_templates.hotel_info %}
 {% endcache %}
 {% endfor %}


 {% endcache %}

Slide 28

Slide 28 text

More tags Less template logic Templates {% for plugin in plugins %}
 {% include 'cms/plugin.html' %}
 {% endfor %}

Slide 29

Slide 29 text

More tags Less template logic Templates 
 CMS._plugins.push(['cms-plugin-{% if generic %}{{ generic.app_label }}-{{ generic.model_name }} -{% if attribute_name %}{{ attribute_name|slugify }}-{% endif %}{% endif %}{{ instance.pk|unlocalize }}', {
 type: {% if generic %}'generic'{% else %}'plugin'{% endif %},
 page_language: '{{ LANGUAGE_CODE }}',
 placeholder_id: '{{ instance.placeholder_id|unlocalize }}',
 plugin_name: '{{ instance.get_plugin_name|default:"" }}',
 plugin_type: '{{ instance.plugin_type }}',
 plugin_id: '{{ instance.pk|unlocalize }}',
 plugin_language: '{{ instance.language|default:"" }}',
 plugin_parent: '{{ instance.parent_id|unlocalize }}',
 plugin_order: '{{ instance.plugin_order }}',{% language request.toolbar.toolbar_language %}
 plugin_restriction: [{% for cls in allowed_child_classes %}"{{ cls }}"{% if not forloop.last %},{% endif %}{% endfor %}],
 plugin_parent_restriction: [{% for cls in allowed_parent_classes %}"{{ cls }}"{% if not forloop.last %},{% endif %}{% endfor %}],
 onClose: {% if refresh_page %}'REFRESH_PAGE'{% else %}{% if redirect_on_close %}'{{ redirect_on_close }}'{% else %}false{% endif %}{% endif %},
 addPluginHelpTitle: '{% trans "Add plugin to" %} {{ instance.get_plugin_name|escapejs }}',
 urls: {
 add_plugin: '{% if add_url %}{{ add_url }}{% else %}{% cms_admin_url "cms_page_add_plugin" %}{% endif %}',
 edit_plugin: '{% if edit_url %}{{ edit_url }}{% elif instance %}{% cms_admin_url "cms_page_edit_plugin" instance.pk %}{% endif %}',
 move_plugin: '{% if move_url %}{{ move_url }}{% else %}{% cms_admin_url "cms_page_move_plugin" %}{% endif %}',
 delete_plugin: '{% if delete_url %}{{ delete_url }}{% elif instance %}{% cms_admin_url "cms_page_delete_plugin" instance.pk %}{% endif %}',
 copy_plugin: '{% if copy_url %}{{ copy_url }}{% else %}{% cms_admin_url "cms_page_copy_plugins" %}{% endif %}'
 } {% endlanguage %}
 }]);


Slide 30

Slide 30 text

More tags Less template logic Templates {% load cms_sample_tags %}
 
 {% render_plugins_js plugins 'en' %}


Slide 31

Slide 31 text

More tags Less template logic Templates PLUGIN_TOOLBAR_JS = "CMS._plugins.push(['cms-plugin-%(pk)s', %(config)s]);"
 
 def get_plugin_toolbar_js(plugin, request_language, children=None, parents=None):
 plugin_name = plugin.get_plugin_name()
 help_text = ugettext('Add plugin to %(plugin_name)s') % {'plugin_name': plugin_name}
 
 data = {
 'type': 'plugin',
 'page_language': request_language,
 'placeholder_id': text_type(plugin.placeholder_id),
 'plugin_name': force_text(plugin_name) or '',
 'plugin_parent': text_type(plugin.parent_id or ''),
 'plugin_restriction': children or [], …
 }
 return PLUGIN_TOOLBAR_JS % {'pk': plugin.pk, 'config': json.dumps(data)}

Slide 32

Slide 32 text

More tags Less template logic Templates @register.simple_tag(takes_context=False)
 def render_plugins_js(context, plugins, language):
 get_toolbar_js = functools.partial(
 get_plugin_toolbar_js,
 request_language=language,
 )
 plugin_js_output = '\n'.join(get_toolbar_js(plugin) for plugin in plugins)
 return mark_safe(plugin_js_output)

Slide 33

Slide 33 text

Third Party Apps

Slide 34

Slide 34 text

Thanks! Paulo Alvarado @czpython bit.ly/djangospeed-pr