SlideShare a Scribd company logo
1 of 71
Custom Signals for Uncoupled Design Bruce Kroeze bruce@ecomsmith.com
I’m going to show youhow to get from this
To this
Without surgery
Or magic
A real world example
(too boring)
A contrived example class PonyForm(forms.Form):    color=forms.CharField(         label='Color',max_length=20,         required=True,         choices=PONY_COLORS)
Might look like Color: White Submit
Adding form flexibility def __init__(self, *args, **kwargs):super(PonyForm, self).__init__(    *args, **kwargs)form_init.send(PonyForm,    form=self)
The Unicorn App def form_init_listener(sender,    form=None, **kwargs):form.fields[’horn'] = forms.CharField(’Horn',      required=True,max_length=20,      choices=HORN_CHOICES)
Linking them form_init.connect(form_init_listener, sender=PonyForm)
Might look like Color: Horn:       White Silver Submit
Promise kept!
The Challenge
Ideal Situation
Custom Signals are a big part of the answer
Best Practices
File Names Signals go in: signals.py Listeners go in: listeners.py
Setup Call “start_listening” in listeners.py from models.py (helps localize imports)
Rules of Thumb Most signals should go in: Models Forms (not so much Views)
What about? That pesky “sender” attribute? If in doubt, use a string. “mysignal.send(‘somelabel’)” Strings are immutable
Questions ?
Examples
(there are goingto be five)
Most of these use“Signals Ahoy”
Example 1:Searching
The View def search(request):     data = request.GET     keywords = data.get('keywords', '').split(' ')     results = {} application_search.send(“search”,        request=request,          keywords=keywords, results=results)     context = RequestContext(request, {             'results': results,             'keywords' : keywords})     return render_to_response('search.html', context)
The Listener def base_search_listener( sender, results={},**kwargs):     results['base'] = 'Base search results'
Search Page   Search Results ,[object Object],[object Object]
Example 2:Url Manipulation
The Base Urls File urlpatterns = patterns('tests.localsite.views',     (r’signalled_view/', ’signalled_view', {}), ) collect_urls.send(sender=localsite,   patterns=urlpatterns)
The Custom App Urls File from django.conf.urls.defaults import * custompatterns = patterns('tests.customapp.views',     (r'^example/$', ‘example', {}),     (r'^async_note/$', 'async_note_create') )
The Listener from urls import custompatterns def add_custom_urls(sender, patterns=(), **kwargs):    patterns += custompatterns
Net Result We’ve defined: ,[object Object]
/example/
/async_note/All at the same level of the URL hierarchy  withoutmanual tweaking or configuration.
Questions ?
Example 3:Views
The View def signalled_view(request): ctx= {         'data' : ‘Not modified'     } view_prerender.send('signalled_view',         context=ctx)     context = RequestContext(request, ctx)     return render_to_response(         ‘signalled_view.html', context)
The Template <div style=“text-align:center”> {{ data }} </div>
Unmodified View Not modified
The Listener def signalled_view_listener(    sender, context={},**kwargs):     context['data'] = “Modified” def start_listening(): view_prerender.connect( signalled_view_listener,        sender=‘signalled_view’)
Modified View Modified
Questions ?
Example 4:Asynchronous
Importing a (big) XLS
The View def locations_upload_xls(request, uuid = None):     if request.method == "POST":         data = request.POST.copy()         form = UploadForm(data, request.FILES)         if form.is_valid(): form.save(request.FILES['xls’], request.user) return HttpResponseRedirect('/admin/location_upload/%s' % form.uuid)     else:         form = UploadForm() ctx= RequestContext(request, {'form' : form})     return render_to_response('locations/admin_upload.html', ctx)
The Form class UploadForm(forms.Form): xls= forms.FileField(label="Excel File", required=True)     def save(self, infile, user): outfile= tempfile.NamedTemporaryFile(suffix='.xls') for chunk in infile.chunks(): outfile.write(chunk) outfile.flush() self.excelfile=outfile form_postsave.send(self, form=self) return True
The Listener def process_excel_listener(sender, form=None, **kwargs):     parsed = pyExcelerator.parse_xls(form.excelfile.name)     # do something with the parsed data – it won’t block processExcelListener = AsynchronousListener(process_excel_listener) def start_listening(): form_postsave.connect(processExcelListener.listen,        sender=UploadForm)
Questions ?
Example 5:Forms(the long one)
The View def form_example(request):     data = {}     if request.method == "POST":         form = forms.ExampleForm(request.POST)         if form.is_valid():             data = form.save()     else:         form = forms.ExampleForm() ctx = RequestContext(request, {         'form' : form,         'formdata' : data })     return render_to_response(‘form_example.html', ctx)
The Form class ExampleForm(forms.Form):     name = forms.CharField( max_length=30, label='Name', required=True)     def __init__(self, *args, **kwargs):         initial = kwargs.get('initial', {}) form_initialdata.send( ExampleForm, form=self, initial=initial) kwargs['initial'] = initial         super(ExampleForm, self).__init__(             *args, **kwargs) signals.form_init.send(ExampleForm, form=self)
The Form (pt 2) def clean(self, *args, **kwargs):     super(ExampleForm, self).clean(*args, **kwargs) form_validate.send(ExampleForm, form=self)         return self.cleaned_data def save(self): data = self.cleaned_data form_presave.send(ExampleForm, form=self) form_postsave.send(ExampleForm, form=self) return self.cleaned_data

More Related Content

What's hot

2014 database - course 2 - php
2014 database - course 2 - php2014 database - course 2 - php
2014 database - course 2 - php
Hung-yu Lin
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
jsmith92
 

What's hot (18)

Moodle Quick Forms
Moodle Quick FormsMoodle Quick Forms
Moodle Quick Forms
 
Sorting arrays in PHP
Sorting arrays in PHPSorting arrays in PHP
Sorting arrays in PHP
 
Functional programming with php7
Functional programming with php7Functional programming with php7
Functional programming with php7
 
PHP Functions & Arrays
PHP Functions & ArraysPHP Functions & Arrays
PHP Functions & Arrays
 
Refactoring using Codeception
Refactoring using CodeceptionRefactoring using Codeception
Refactoring using Codeception
 
PHP for Python Developers
PHP for Python DevelopersPHP for Python Developers
PHP for Python Developers
 
Class 4 - PHP Arrays
Class 4 - PHP ArraysClass 4 - PHP Arrays
Class 4 - PHP Arrays
 
COMP2021 Final Project - LightHTML
COMP2021 Final Project - LightHTMLCOMP2021 Final Project - LightHTML
COMP2021 Final Project - LightHTML
 
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
 
2014 database - course 2 - php
2014 database - course 2 - php2014 database - course 2 - php
2014 database - course 2 - php
 
2013 28-03-dak-why-fp
2013 28-03-dak-why-fp2013 28-03-dak-why-fp
2013 28-03-dak-why-fp
 
Inc
IncInc
Inc
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
Arrays in php
Arrays in phpArrays in php
Arrays in php
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
 
Migrare da symfony 1 a Symfony2
 Migrare da symfony 1 a Symfony2  Migrare da symfony 1 a Symfony2
Migrare da symfony 1 a Symfony2
 
D8 Form api
D8 Form apiD8 Form api
D8 Form api
 
Graph Database Query Languages
Graph Database Query LanguagesGraph Database Query Languages
Graph Database Query Languages
 

Viewers also liked

Agenzie Informagiovani
Agenzie InformagiovaniAgenzie Informagiovani
Agenzie Informagiovani
guestf240ad43
 

Viewers also liked (17)

Overdrive
OverdriveOverdrive
Overdrive
 
Parc
ParcParc
Parc
 
INSM as first Magnolia 4.0 reference
INSM as first Magnolia 4.0 referenceINSM as first Magnolia 4.0 reference
INSM as first Magnolia 4.0 reference
 
Avantaj la start 2010
Avantaj la start 2010Avantaj la start 2010
Avantaj la start 2010
 
Social Networking
Social NetworkingSocial Networking
Social Networking
 
AreYouSafe? -- Municipal and Federal Data Extraction, Mash-up, and Visualization
AreYouSafe? -- Municipal and Federal Data Extraction, Mash-up, and VisualizationAreYouSafe? -- Municipal and Federal Data Extraction, Mash-up, and Visualization
AreYouSafe? -- Municipal and Federal Data Extraction, Mash-up, and Visualization
 
How To Upload Photos To Facebook
How To Upload Photos To FacebookHow To Upload Photos To Facebook
How To Upload Photos To Facebook
 
Brandul Personal sau Cine sunt eu
Brandul Personal sau Cine sunt euBrandul Personal sau Cine sunt eu
Brandul Personal sau Cine sunt eu
 
Skip Count Mazes
Skip Count MazesSkip Count Mazes
Skip Count Mazes
 
Prezentare public speaking
Prezentare public speakingPrezentare public speaking
Prezentare public speaking
 
Top 10 Reasons for ERP Project Failure
Top 10 Reasons for ERP Project FailureTop 10 Reasons for ERP Project Failure
Top 10 Reasons for ERP Project Failure
 
Supply Chain Process Improvement Methodology V1
Supply Chain Process Improvement Methodology V1Supply Chain Process Improvement Methodology V1
Supply Chain Process Improvement Methodology V1
 
Segundas olimpiadas liceos municipales paine 2016
Segundas olimpiadas liceos municipales paine 2016Segundas olimpiadas liceos municipales paine 2016
Segundas olimpiadas liceos municipales paine 2016
 
Kukla Tiyatrosu
Kukla TiyatrosuKukla Tiyatrosu
Kukla Tiyatrosu
 
Erp Implementation Methodology Wkshp 2.0 120611
Erp Implementation Methodology Wkshp 2.0 120611Erp Implementation Methodology Wkshp 2.0 120611
Erp Implementation Methodology Wkshp 2.0 120611
 
Agenzie Informagiovani
Agenzie InformagiovaniAgenzie Informagiovani
Agenzie Informagiovani
 
diapositiva
diapositivadiapositiva
diapositiva
 

Similar to Custom Signals for Uncoupled Design

Php Reusing Code And Writing Functions
Php Reusing Code And Writing FunctionsPhp Reusing Code And Writing Functions
Php Reusing Code And Writing Functions
mussawir20
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicate
Kiev ALT.NET
 
GHC Participant Training
GHC Participant TrainingGHC Participant Training
GHC Participant Training
AidIQ
 
Scripting GeoServer with GeoScript
Scripting GeoServer with GeoScriptScripting GeoServer with GeoScript
Scripting GeoServer with GeoScript
Justin Deoliveira
 

Similar to Custom Signals for Uncoupled Design (20)

Django Forms: Best Practices, Tips, Tricks
Django Forms: Best Practices, Tips, TricksDjango Forms: Best Practices, Tips, Tricks
Django Forms: Best Practices, Tips, Tricks
 
Drupal Form Api
Drupal Form ApiDrupal Form Api
Drupal Form Api
 
Django Search
Django SearchDjango Search
Django Search
 
Php Reusing Code And Writing Functions
Php Reusing Code And Writing FunctionsPhp Reusing Code And Writing Functions
Php Reusing Code And Writing Functions
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicate
 
Framework
FrameworkFramework
Framework
 
Sencha Touch Intro
Sencha Touch IntroSencha Touch Intro
Sencha Touch Intro
 
Writing Apps the Google-y Way
Writing Apps the Google-y WayWriting Apps the Google-y Way
Writing Apps the Google-y Way
 
Drupal Lightning FAPI Jumpstart
Drupal Lightning FAPI JumpstartDrupal Lightning FAPI Jumpstart
Drupal Lightning FAPI Jumpstart
 
Creating Domain Specific Languages in Python
Creating Domain Specific Languages in PythonCreating Domain Specific Languages in Python
Creating Domain Specific Languages in Python
 
Introduction to Prototype JS Framework
Introduction to Prototype JS FrameworkIntroduction to Prototype JS Framework
Introduction to Prototype JS Framework
 
Plone For Developers - World Plone Day, 2009
Plone For Developers - World Plone Day, 2009Plone For Developers - World Plone Day, 2009
Plone For Developers - World Plone Day, 2009
 
PHP Workshop Notes
PHP Workshop NotesPHP Workshop Notes
PHP Workshop Notes
 
The bones of a nice Python script
The bones of a nice Python scriptThe bones of a nice Python script
The bones of a nice Python script
 
HTML::FormHandler
HTML::FormHandlerHTML::FormHandler
HTML::FormHandler
 
Form
FormForm
Form
 
Think Generic - Add API's To Your Custom Modules
Think Generic - Add API's To Your Custom ModulesThink Generic - Add API's To Your Custom Modules
Think Generic - Add API's To Your Custom Modules
 
GHC Participant Training
GHC Participant TrainingGHC Participant Training
GHC Participant Training
 
Scripting GeoServer with GeoScript
Scripting GeoServer with GeoScriptScripting GeoServer with GeoScript
Scripting GeoServer with GeoScript
 
vfsStream - a better approach for file system dependent tests
vfsStream - a better approach for file system dependent testsvfsStream - a better approach for file system dependent tests
vfsStream - a better approach for file system dependent tests
 

Recently uploaded

Recently uploaded (20)

Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
A Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source MilvusA Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source Milvus
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 

Custom Signals for Uncoupled Design

  • 1. Custom Signals for Uncoupled Design Bruce Kroeze bruce@ecomsmith.com
  • 2. I’m going to show youhow to get from this
  • 3.
  • 5.
  • 8.
  • 9. A real world example
  • 11. A contrived example class PonyForm(forms.Form): color=forms.CharField( label='Color',max_length=20, required=True, choices=PONY_COLORS)
  • 12. Might look like Color: White Submit
  • 13.
  • 14. Adding form flexibility def __init__(self, *args, **kwargs):super(PonyForm, self).__init__( *args, **kwargs)form_init.send(PonyForm, form=self)
  • 15. The Unicorn App def form_init_listener(sender, form=None, **kwargs):form.fields[’horn'] = forms.CharField(’Horn', required=True,max_length=20, choices=HORN_CHOICES)
  • 17. Might look like Color: Horn: White Silver Submit
  • 18.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26. Custom Signals are a big part of the answer
  • 28. File Names Signals go in: signals.py Listeners go in: listeners.py
  • 29. Setup Call “start_listening” in listeners.py from models.py (helps localize imports)
  • 30. Rules of Thumb Most signals should go in: Models Forms (not so much Views)
  • 31. What about? That pesky “sender” attribute? If in doubt, use a string. “mysignal.send(‘somelabel’)” Strings are immutable
  • 33.
  • 35. (there are goingto be five)
  • 36. Most of these use“Signals Ahoy”
  • 37.
  • 39.
  • 40. The View def search(request): data = request.GET keywords = data.get('keywords', '').split(' ') results = {} application_search.send(“search”, request=request, keywords=keywords, results=results) context = RequestContext(request, { 'results': results, 'keywords' : keywords}) return render_to_response('search.html', context)
  • 41. The Listener def base_search_listener( sender, results={},**kwargs): results['base'] = 'Base search results'
  • 42.
  • 44.
  • 45. The Base Urls File urlpatterns = patterns('tests.localsite.views', (r’signalled_view/', ’signalled_view', {}), ) collect_urls.send(sender=localsite, patterns=urlpatterns)
  • 46. The Custom App Urls File from django.conf.urls.defaults import * custompatterns = patterns('tests.customapp.views', (r'^example/$', ‘example', {}), (r'^async_note/$', 'async_note_create') )
  • 47. The Listener from urls import custompatterns def add_custom_urls(sender, patterns=(), **kwargs): patterns += custompatterns
  • 48.
  • 50. /async_note/All at the same level of the URL hierarchy withoutmanual tweaking or configuration.
  • 53.
  • 54. The View def signalled_view(request): ctx= { 'data' : ‘Not modified' } view_prerender.send('signalled_view', context=ctx) context = RequestContext(request, ctx) return render_to_response( ‘signalled_view.html', context)
  • 55. The Template <div style=“text-align:center”> {{ data }} </div>
  • 57. The Listener def signalled_view_listener( sender, context={},**kwargs): context['data'] = “Modified” def start_listening(): view_prerender.connect( signalled_view_listener, sender=‘signalled_view’)
  • 61.
  • 63. The View def locations_upload_xls(request, uuid = None): if request.method == "POST": data = request.POST.copy() form = UploadForm(data, request.FILES) if form.is_valid(): form.save(request.FILES['xls’], request.user) return HttpResponseRedirect('/admin/location_upload/%s' % form.uuid) else: form = UploadForm() ctx= RequestContext(request, {'form' : form}) return render_to_response('locations/admin_upload.html', ctx)
  • 64. The Form class UploadForm(forms.Form): xls= forms.FileField(label="Excel File", required=True) def save(self, infile, user): outfile= tempfile.NamedTemporaryFile(suffix='.xls') for chunk in infile.chunks(): outfile.write(chunk) outfile.flush() self.excelfile=outfile form_postsave.send(self, form=self) return True
  • 65. The Listener def process_excel_listener(sender, form=None, **kwargs): parsed = pyExcelerator.parse_xls(form.excelfile.name) # do something with the parsed data – it won’t block processExcelListener = AsynchronousListener(process_excel_listener) def start_listening(): form_postsave.connect(processExcelListener.listen, sender=UploadForm)
  • 68.
  • 69. The View def form_example(request): data = {} if request.method == "POST": form = forms.ExampleForm(request.POST) if form.is_valid(): data = form.save() else: form = forms.ExampleForm() ctx = RequestContext(request, { 'form' : form, 'formdata' : data }) return render_to_response(‘form_example.html', ctx)
  • 70. The Form class ExampleForm(forms.Form): name = forms.CharField( max_length=30, label='Name', required=True) def __init__(self, *args, **kwargs): initial = kwargs.get('initial', {}) form_initialdata.send( ExampleForm, form=self, initial=initial) kwargs['initial'] = initial super(ExampleForm, self).__init__( *args, **kwargs) signals.form_init.send(ExampleForm, form=self)
  • 71. The Form (pt 2) def clean(self, *args, **kwargs): super(ExampleForm, self).clean(*args, **kwargs) form_validate.send(ExampleForm, form=self) return self.cleaned_data def save(self): data = self.cleaned_data form_presave.send(ExampleForm, form=self) form_postsave.send(ExampleForm, form=self) return self.cleaned_data
  • 73. The Listeners def form_initialdata_listener( sender, form=None, initial={}, **kwargs): initial['email'] = "a@example.com" initial['name'] = 'test' def form_init_listener( sender, form=None, **kwargs): form.fields['email'] = forms.EmailField( 'Email', required=True)
  • 74. The Listeners (pt2) def form_validate_listener( sender, form=None, **kwargs): """Do custom validation on form""" data = form.cleaned_data email = data.get('email', None) if email != 'test@example.com': errors = form.errors if 'email' not in errors: errors['email'] = [] errors['email'].append( 'Email must be "test@example.com"')
  • 78. Resources Signals Ahoy: http://gosatchmo.com/apps/django-signals-ahoy This presentation:http://ecomsmith.com/2009/speaking-at-djangocon-2009/
  • 79. Photo Credits Pony/Unicorn: Bruce Kroeze (pony property of Mia Kroeze) Gnome: Bruce Kroeze Fork: Foxuman (sxc.hu) Monkey: Lies Meirlaen Air horns: AdrezejPobiedzinski
  • 80. Photo Credits 2 Pirate Ship: Crystal Woroniuk Telescope: Orlando Pinto Dominoes: Elvis Santana San Miguel Panorama: Bruce Kroeze Birds on wire: Jake P (sxc.hu) Feedback Form, “Excellent”: DominikGwarek