Attaching a .png file to an email sent by satchmo - django

In a Satchmo Store, I need to attach a small .png (a barcode) to an email that django sends on completion of the order. The email is formatted in HTML using send_order_confirmation() which calls send_store_mail() (both part of satchmo.) Neither of these functions offer the ability to attach a file (I think) so should I just re-write them? I was wondering if it is possible/better to do this using signals. Maybe rendering_store_mail() ?
By the way, the barcode would be dynamically generated, so there's no way of having a link to a file on a server somewhere.
Many thanks,
Thomas

well I too had to add extra infos to the confirmation emails, only text though. So this would be the very easy way to add extra-stuff to emails using signals, which IMHO, is the best approach to do it. Always use signals if you can avoid overriding the satchmo-core ;-)
define your listener to add some context for the rendering. In this case I'm adding the contents of an extra notes field, and the barcode for this order, supposing there is a function named get_barcode_img(<order>), to the context. I'm supposing here too, that the get_barcode_img function would return not just a png, but something like a MIMEImage (like from email.MIMEImage import MIMEImage) to be able to just include it inline. Also, there might be more infos needed, like a MIME-header for the img.
# localsite/payment/listeners.py
def add_extra_context_to_store_mail(sender,
send_mail_args={}, context={}, **kwargs):
if not 'order' in context:
return
context['barcode_header'] = get_barcode_header(context['order'])
context['barcode_img'] = get_barcode_img(context['order'])
context['notes'] = context['order'].notes
connect the listener to the signal somewhere where the code will be "discovered" for sure, like models.py:
# localsite/payment/models.py
from satchmo_store.shop.signals import rendering_store_mail, order_notice_sender
rendering_store_mail.connect(payment_listeners.add_extra_context_to_store_mail, sender=order_notice_sender)
override the templates locally (e.g. order_placed_notice.html) to add the new context. Be aware where you put your templates, as the path is essential for django to take your new template instead of the satchmo's one. In this case, starting from your project's root-path, there could be a templates-folder and inside it, there must be exactly the same path as in the satchmo-folder. E.g. /templates/shop/email/order_placed_notice.html ... this can be applied for any "valid" templates-folder inside an app. It's up to you to decide, where/how the templates should be organized.
<!-- templates/shop/email/order_placed_notice.html -->
<!DOCTYPE ...><html>
<head>
<!-- include the image-header here somewhere??? -->
<title></title>
</head>
<body>
...
Your comments:
{{ notes }}
Barcode:
{{ barcode_img }}"

Related

How to Pass a Python Variable to a Template in another app in Django

I have views in app_1 which returns a dictionary and I want to pass it in the html in another app called app_2. I tried using {{dic_name.value_1}} and also like {{app_name:dic_name.value_1}}, but it didn't work.
-app_1
--urls.py
--views.py
-app_2
--templates
--app_2
-- app_2.html
my view in app_1
def index(request):
user_info={}
user_info['name']= 'joe'
user_info['age'] = 23
return JsonResponse(user_info)
my Html in app2 is like
<body>
<h1>Name</h1>: {{user_info.name}}
<h1>Ange</h1>: {{user_info.age}}
</body>
Can anyone help to solve this problem. Thanks
First a couple comments:
I have views in app_1 which returns a dictionary
Nope, it returns an HTTP response (with json content and application/json as content-type).
and I want to pass it in the html in another app called app_2.
That's just not how it works. A Django view returns a HTTP response, period (wether the response's body contains HTML and wether this HTML has been built using a template are totally irrelevant here).
I tried using {{dic_name.value_1}} and also like {{app_name:dic_name.value_1}}, but it didn't work.
Programming by accident never really works. You'll get better results - and you'll get them faster - by understanding how things work and do the right thing right from the start.
NB : Sorry if this looks a bit patronizing, please don't take it as a personal offense, I'm really trying to help you with those comments.
Now for the proper answer:
For the general case, when you have a piece of data (an object, a list, a dict, whatever) that you want to make accessible to different unrelated templates, you mainly have two solution:
a context processor
a custom templatetag
A context processor will be invoked for all templates rendering (well, almost, cf the doc), so it's the right solution when you want this "piece of data" to be available everywhere "out of the box". The downside is that you'll get a performance hit for each and every template. Also, a context processor only has access to the request - it can't access the template's context.
A custom templatetag is only executed when called from a template, so it's a better option if the "piece of data" you want is costly to build and is only used in a few templates. Also it can take arguments from the template, including the full context.
From a maintainability / reusability POV, whether you go for a processor or templatetag, it's usually better to factor out the code producing the desired value(s) into a dedicated utility function and to call that functions from the processor or tag.
In your case, your app_1.views.index_view returns a JsonResponse, so factoring out this part of code is indeed the best solution:
app_1/utils.py:
def get_user_info():
return {"name": "Joe", "age": 42}
app_1/views.py:
from app_1.utils import get_user_info
def index(request):
user_info = get_user_info()
return JsonResponse(user_info)
app_1/templatags/app_1.py:
# you'll need this if using python 2.7.x
# else the import will fail
from __future__ import absolute_imports
from django import template
from app_1.utils import get_user_info
register = template.Library()
#register.simple_tag
def user_info():
return get_user_info()
app2 template.html
{% load user_info from app_1 %}
{% user_info as ui %}
<dl>
<dt>Name</dt><dd>{{ ui.name }}</dd>
<dt>Age</dt><dd>{{ ui.age }}</dd>
</dl>
This is of course a dumbed down example (and it might have a couple bugs, as all made up examples) and your real code will certainly be a bit more involved, but this should at least get you started.

Edit css files from django admin

My client wants to edit the css files of the site from django admin.
Is there any way to do it ? .
Basically what they want is,to be able to change the color,font etc of the data in the front end from django admin interface.
The best thing would be to just let him edit the css file itself. CSS is, in essence, a rather flexible tool, so writing a way to manage it is rather tough (and really, overkill). It's already easy to pick-up, and any nice editor like sublime or notepad++ would probably be easier and more natural than whatever you'll build using the admin site. Also, by building a simple way to control css, your client will probably start asking for more and more flexibility until you find yourself building an entire cms (trust me, I've been there myself).
What's more, your client probably only wants to manage small aspects or details of the site. Recently I had a project where I allowed my users to style their display of my application. The way I did it was to create a UserDesign model which extended the base User model and kept very specific css data. Something like this:
class UserDesign(models.Model):
user = models.OneToOneField(User)
background_color = models.CharField(max_length=15)
font_color = models.CharField(max_length=20, choices=COLORS)
theme = models.CharField(max_length=20, choices=THEMES)
Meaning, they didn't control the entirety of the css, but they did get to choose the background color and some other information. It's a very neat addition to any website. However, if you are bent over doing it the hard way, I'd do something like this:
class Selector(models.Model):
name = models.CharField(max_length=30)
def get_template(self):
attrs = [a.join() for a in self.attr_set.all()]
return """ %s { %s } """ % ( self.name, ';'.join(attrs) )
class Attr(models.Model):
key = models.CharField(max_length=30)
value = models.CharField(max_length=30)
selector = models.ForeignKey(Selector)
def join(self):
return ': '.join(self.key, self.value)
I chose 30 as the max_length completely arbitrarily (you might need it longer), and you can use a TabularInline to make each selector easy to manage. Then you can easily use different css definitions inside your templates themselves:
<style>
{% for selector in selectors %}
{{ selector.get_template }}
{% endfor %}
</style>
Of course, the Selector model would probably need another field called 'template' or 'view' or something, to link it to a certain html file, though at this point it quickly start devolving into building your own cms (which, as mentioned before, is quite a headache that not wanting to edit a text file just doesn't justify)
A third viable option is to create a view with a code-editor, and just let your client edit his css through the web page. There's more than enough client-side plugins out there, like ace or codemirror (and of course, limit that view to administrators, which very simple to do).

Django's equivalence of ASP.NET UserControl

If anyone here is ASP.NET pro, you might know what I mean by user control. I wish to create a similar one in django instead.
So, my problem is that I have several pages in my website, but I need a search bar to appear in every pages. Since I require the views.py to operate this search bar, I cannot do a simple method of
{% include 'something.html' %}
Therefore, can anyone suggest how can I do it?
There are a couple of ways to accomplish what you're wanting to do:
Context Processors
Template Tags
Context Processors can augment the template context with values, regardless of which template is loaded. They are akin to filters in Rails.
Template Tags, like Context Processors, can accomplish anything you can do in Python, but are implemented at the template level.
If you need something to be present on every template, one of the simplest ways to accomplish this is with an inclusion tag, which can also accept values passed to it. An inclusion tag could be implemented at your highest level template, a.k.a your MasterPage, and as long as you don't put it in a block and override it, it would appear on every page that includes that template in its inheritance chain.
If it's just something you want to include on every page, and it doesn't need to do any processing, you should just be able to place the code you want in the top-most template and have subsequent templates inherit that.
I typically have a "base.html" template that all of my templates inherit from. If I need something to be in every page, I put it there. If it's something I want there by default, but want to be able to augment it in subsequent templates, I will place it into a block. That block will let you include or override its default content.
I know this post is kind of old but I just came across it and found a kind-of-solution that works. I call it kind-of-solution because it is a workaround.
I have a few different sites on which I want to display logging information. This display always looks the same (it has the same html) and has the same database table and model class behind it.
My solution/workaround uses the django filters:
in views.py I put the list of log-entries in the context
context = {'list_log': Log.objects.filter(condition = True) }
template = loader.get_template('my_html_file.html')
return HttpResponse(template.render(context, request))
in my_html_file.html I use a custom filter
{{ list_log|get_log_uc|safe }}
in the filters.py I load another html file with this custom filter
#register.filter
def get_log_uc(list_log):
template = loader.get_template('user_control_log.html')
context = { 'list_log' : log }
return template.render(context)
in user_control_log.html I have the user control equivalent html
{% for log in list_log %}
<p>log.something</p>
{% endfor %

Customizing Django.contrib.comments honeypot

I'm using Django's standard comment system and I would like to extend its anti-spam honeypot capability.
I thought of changing the default "name" and "id" of the field to something more alluring for spam-bots such as "website". I checked the html and this looks like this:
<p style="display:none;">
<label for="id_honeypot">Never send a human to do a machine's job</label>
<input type="text" name="honeypot" id="id_honeypot" />
</p>
Am I correct in thinking that changing the defaults of this element would boost its anti-spam capabilities? I tried modifying it in the django/contrib/comments/forms.py like this:
class CommentForm(CommentDetailsForm):
#use to be honeypot = forms.CharField(...
website = forms.CharField(required=False,
label=_('Never send a human to do a machines job')
def clean_honeypot(self):
"""Check that nothing's been entered into the honeypot."""
value = self.cleaned_data["website"]
if value:
raise forms.ValidationError(self.fields["website"].label)
return value
And this successfully changes the name and id in the html generated by django BUT then the whole mechanism stops working - I tried populating this invisible field, submitted and the comment was added.
I have a few other ideas as well, but first I'd really like to get this working - is it possible to modify the default honeypot name and id AND have it working like it should?
P.S I believe a more elegent way of doing this would be to extend django.contrib.comments and code the modification there instead of working on actual django code - what would be the best way of accomplishing this?
Given a bit more time to tinker around I found the answer to both of my questions:
In order to modify the standard honeypot or to create your own, you have to extend the CommentForm class by adding a clean_NAME_OF_HONEYPOT function as well as a NAME_OF_HONEYPOT variable both of which look similar to the standard ones and you also have to override the security_errors function to include the name of your new/modified honeypot in the dictionary.
The best way to do this is to create your custom comments app as described here: https://docs.djangoproject.com/en/dev/ref/contrib/comments/custom/ .
I hope this answer helps anyone else in my situation.

Flex 4 + Django: Testing and Release Options

I am creating a django-based site that will serve flash apps that occasionally access data via pyamf. I need to be able to easily test the flash in the context of the django framework, i.e. with all the login cookies and everything available, so that when I make a pyamf call, it has all the user context there. And I need to be able to test and release both the swf's and the wrapper html's in a sane way. However:
The html templates in flex are already templates, so if I put template code in there for django, it gets scraped out before the flashapp.html is created.
The html's and swf's automatically get released to the same directory, but I want them to go to different directories because the swf's shouldn't be served by django and the html's should be in an area in control of django.
This leads me to believe, at first glance, that I need:
A way of having the html and swf files be released to different locations. (I don't know how to do this.)
A way of releasing the html's as stubs (no html/body tag) so that I can include them from another location in django. (I guess just strip what I don't want from the index.template.html?)
Then I can point flex to go to the django site that in turn includes the generated flashapp.html that in turn references the swf, and it should all work. (By feeding that alternate html to the run/debug settings, I assume.)
So my question comes down to:
Is the above the best way of doing this, or is this even the right direction?
If so, how do I release the html and swf to different directories? (For debug and release mode, if there are two different methods.)
If not, what is proper?
And if there are any other general bits of advice for me on this topic, please feel free to share. :-)
Finally figured this out myself. A combination of this and django get-parameters works. The general take-away:
You can put {% tags %} and {{ variables }} in index.template.html without worry, as there is no way to customize the currently-existing macros there like ${title}
If you make a foo.template.html and foo-debug.template.html in the html-template directory of your project, then the former will override index.template.html for release builds, and the latter for debug builds (note that the result will be foo-debug.html instead of foo.html though.)
You can pass the name of the SWF in a parameter to django, and have it fill in the directory for you
foo-debug.template.html
<object ...
<param name="movie" value="{{ bin_debug_url }}/${swf}.swf" ...
djangoflash.html
{% block content %}
{% include flash_template %}
{% endblock %}
views.py
def djangoflashview( request, **kwargs ):
if not kwargs.has_key('extra_context'):
kwargs['extra_context'] = {}
if request.GET.has_key('name'):
debug = "-debug" if request.GET.has_key('debug') else ""
bin_debug_dir = '/dir-to-bin-debug/'
bin_debug_url = 'url-to-bin-debug'
name = bin_debug_dir + request.GET['name'] + debug + '.html'
kwargs['extra_context']['flash_template'] = name
kwargs['extra_context']['bin_debug_url' ] = bin_debug_url
return direct_to_template( request, **kwargs )
urls.py
url( r'^djangoflash/', 'views.djangoflashview',
{ 'template': 'djangoflash.html' }
foo.mxml's run-debug target:
/url-to-django/djangoflash/?name=foo
When you debug foo.mxml, flex:
Adds &debug=true to the url
Brings up a browser to /url-to-djangoflash/djangoflash/?name=foo&debug=true
Which picks djangoflash/ in urls.py
Which passes the request to djangoflashview and {'name':'foo','debug':'true'} to request.GET in views.py
Which figures out the name and location of the foo-debug.html location, passing it to the flash_template context variable
And the url of the swf to the bin_debug_url context variable
And loads up the direct template djangoflash.html
Which, in djangoflash.html, includes the foo-debug.html wrapper for flash using the flash_template context variable
Which, in turn fills in the bin_debug_url context variable to point the foo.swf reference correctly to the thing you just compiled
Whew. :-P