Download - Django Good Practices
![Page 1: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/1.jpg)
- Good Practices
selectively and subjectively..
2013
![Page 2: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/2.jpg)
Justyna Żarna Woman in Django / Python World
@Ustinez
http://solution4future.com
Who?
![Page 3: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/3.jpg)
1. Models:a) a signal to warm-up,b) migrations,c) let's focus on User.
2. Views:a) class-based views.
3. Templates:a) customization.
3. Tools:a) no one likes queue..,b) extend and explore.
About?
![Page 4: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/4.jpg)
1. Save vs Signals:
Model layer - the warm up
good usage
bad usage
a common mistake: forgetting *args, **kwargs
def save(self, *args, **kwargs):#we already have predefined sponsor's types, so we can't add next the same type
if self.type in ("Silver", "Gold", "Platinum"): return else: super(Sponsor, self).save(*args, **kwargs)
def save(self, *args, **kwargs):self.increment_sponsors_number()
super(Event, self).save(*args, **kwargs)
![Page 5: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/5.jpg)
1. Emit a pre-signal.
2. Pre-process the data (automated modification for "special behavior fields").
for example:DateField with attribute: auto_now = True
3. Preparation the data for the database.for example:DateField and datetime object is prepared to date string in ISO, but simple data types are ready
4. Insert to database.
5. Emit a post-signal.
When an object is saving..
![Page 6: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/6.jpg)
1. Realization of Observer design pattern.2. Applications, pieces of code get notifications about defined actions.3. List of available signals:
a) pre_init, post_init,b) pre_save, post_save,c) pre_delete, post_delete,d) m2m_changed,e) request_started, request_finished.
4. Listening to signals.
Django is sending us the signal..
@receiver(post_save, sender=Event)def hello_function(sender, **kwargs)
print ('Hello! I'm new Event!')
write your own signal
def hello_function(Sponsor, **kwargs):
print ('Hello. I'm new Sponsor!')post_save.connect(hello_function)
![Page 7: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/7.jpg)
1. Separate process, threads depend on shared resource.
2. When operations are atomic, shared resource is safe.
3. Problem is difficult to debug, non deterministic and depends on the relative interval of access to resource.
4. Good designed project avoids this problem.
Example:Gamification, Scoring System, Optimalization, Statistics,
Raports etc etc..
Race condition problem
![Page 8: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/8.jpg)
class Movie(models.Model) title = models.CharField(max_length = 250, verbose_name='title')
lib = models.ForeignKey(Library, verbose_name='library')
class Library movie_counter = models.IntegerField(default = 0, 'how many movies?')
Compile all information
@receiver(post_save, sender=Movie) def add_movie_handler(sender, instance, created, **kwargs): if created: instance.lib.movie_counter += 1 instance.lib.save()
@receiver(post_save, sender=Movie) def add_movie_handler(sender, instance, created, **kwargs): if created: instance.lib.movie_counter = F(movie_counter) + 1 instance.lib.save()
![Page 9: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/9.jpg)
1. Schemamigration:Schema evolution, upgrade database schema, history of changes and possibility to move forward and backward in this history.
./manage.py schemamigration myapp --auto
2. Datamigration:Data evolution.
./manage.py datamigration myapp update_title
def forwards(self, orm):for lib in Library.objects.all():
lib.movie_counter = 100lib.save()
Migrations - South
![Page 10: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/10.jpg)
3. Updating migrations:./manage.py schemamigration myapp --auto --updateor..../manage.py shellform south.models import MigrationHistorymigration = MigrationHistory.objects.get(migration = "example_name")migration.delete()
4. Team work - merging migrations:./manage.py migrate --list./manage.py schemamigration --empty myapp merge_models
5. Migration:./manage.py migrate
5. Fixtures - yaml serialization.
Migration - south
![Page 11: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/11.jpg)
Before 1.5
class Profile(models.Model):user = models.OneToOneField(User, related_name='profile')
city = models.CharField(max_length=255, verbose_name=u'city')street = models.CharField(max_length=255, verbose_name=u'street')house_number = models.CharField(max_length=255, verbose_name=u'home
nr') zip_code = models.CharField(max_length=6, verbose_name=u'zip code')
Focus on old User
class MyUser(User):def city_and_code(self):
return '%s %s' % (self.city, self.zip_code)class Meta:
verbose_name = u"User" verbose_name_plural = u"Users" db_table = "account_profile" proxy = True
![Page 12: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/12.jpg)
Django 1.51. The simplest way to customize User:class MyUser(AbstractBaseUser): uuid = models.CharFieldField(max_length = 10, unique = True, verbose_name = "user uuid")
USERNAME_FIELD = (uuid) # unique identifierREQUIRED_FIELDS = ('first_name') # mandatory fields for createsuperuser
management command
Focus on new User
AbstractBaseUser provides only core implementation of User with hashed passwords and password reset and only few basic method like: is_authenticated(), get_full_name(), is_active() etc.
AbstractBaseUser is dedicated simple changes for User, not creating full profile.
![Page 13: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/13.jpg)
2. Extend User like profile:class MyUser(AbstractUser): city = models.CharField(max_length=255, verbose_name=u'city')
street = models.CharField(max_length=255, verbose_name=u'street')house_number = models.CharField(max_length=255, verbose_name=u'home number')
zip_code = models.CharField(max_length=6, verbose_name=u'zip code')
Focus on new User
AbstractUser provides full implementation of the Django's default User and UserAdmin is available.
![Page 14: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/14.jpg)
Functional approach:
def get_big_libs(request):context = {}context['libs'] = lLibrary.objects.filter(movie_counter__gt = 120)
return render_to_response('template_name.html', {'context': context}, context_instance=RequestContext(request))
Class-based views
Problems:1. A repetitive constructions...2. Low reusability of the code...3. Negation of the principle of DRY...4. Bad readability, monotonicity...
![Page 15: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/15.jpg)
New approach:from django.conf.urls import patterns, url, includefrom django.views.generic import ListViewfrom myapp.models import Libraryurlpatterns = patterns('', (r'^library/$', ListView.as_view(
queryset=Library.objects.filter(movie_counter__gt = 120),context_object_name="libs_list")),
)
Class-based views
Extending example:urlpatterns = patterns('', (r'^library/(?P<counter>\d+)/$', LibraryListView.as_view()),)class LibraryListView(ListView): context_object_name = "libs_list" template_name = "libs.html" def get_queryset(self): return Library.object.filter(movie_counter__gt=self.args[0])
![Page 16: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/16.jpg)
1. Class with all benefits:a) inheritance.
2. Generalization.
3. DRY.
4. Fast and simple for common usage.
5. Fast prototyping.
6. Easy and clean extending and customization.
Advantages
![Page 17: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/17.jpg)
Approach lazy developer:def view1(request):
context = {}context['config'] = {'a': [1,2,3], 'b': ['a', 'b', 'c', 'd', 'e', 'f'']}context['key'] = request.GET.get('key')context['key_options_from_config'] = context['config'][context['key']]return render_to_response('template_name.html', {'context': context})
In template: {% if key_options_from_config %} TODO {% endif %}
Templates - custom tags and filters
reusable snippetreusable filter
Other really lazy developer write this:@register.filter(name='get_val')def val(value, key):
return value[key] if value.get(key) else False
TEMPLATE:{% if config|get_val:key %}
TODO {% endif %}
![Page 18: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/18.jpg)
Celery is an asynchronous tasks queue.
Celery in meantime of request send some async tasks to queue and do some computation.
Celery is working with Kombu and RabbitMQ and various backends for example Redis etc..
Destination: messaging systems, cron task, calculations etc..
No one likes queue?
![Page 19: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/19.jpg)
Examples:
1. Cron jobs in Python code (no need to configurate or order cron jobs on server or writing command).
2. Throttled tasks.
3. Delayed tasks.
4. Move heavy load jobs to workers on other machines. (application will not suffer on preformance).
5. Chaining tasks.
Celery tasks
![Page 20: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/20.jpg)
@task def delete_fake_libraries():
for lib in Library.objects.all():if lib.movie_counter == 0 and lib.book_counter == 0:
lib.delete()
CELERYBEAT_SCHEDULE = { 'check_campaign_active': {'task': myapp.tasks.delete_fake_libraries', 'schedule': crontab(minute='59', hour='23'), 'args': None},}
In action
![Page 21: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/21.jpg)
photo by Grzegorz Strzelczyk
Explore..
![Page 22: Django Good Practices](https://reader034.vdocuments.site/reader034/viewer/2022052204/555673cdd8b42a144c8b52ad/html5/thumbnails/22.jpg)
Thank you :))