Signals are a very powerful tool available in Django that allows you to decouple aspects of your application. The Django Signals Documentation, has this summary:
“In a nutshell, signals allow certain senders to notify a set of receivers that some action has taken place.”
In addition to all of the built in Django signals, Satchmo includes a number of store related signals. By using these signals, you can add very unique customizations to your store without needing to modify the Satchmo code.
Sent after a value from the configuration application has been changed.
Arguments sent with this signal:
- sender
- The instance of livesettings.values.Value that was changed
- old_value
- The old value of the setting
- new_value
- The new value of the settings
- setting
- The instance of livesettings.values.Value that was changed (Note: this argument is the same as sender)
Sent when an order is complete and the balance goes to zero during a save.
Arguments sent with this signal:
- sender
- The instance of satchmo_store.shop.models.Order that was successful
- order
- The instance of satchmo_store.shop.models.Order that was successful (Note: this argument is the same as sender)
Sent when an order is about to be cancelled and asks listeners if they allow to do so.
By default, orders in states ‘Shipped’, ‘Completed’ and ‘Cancelled’ are not allowed to be cancelled. The default verdict is stored in order.is_cancellable flag. Listeners can modify this flag, according to their needs.
Arguments sent with this signal:
- sender
- The instance of satchmo_store.shop.models.Order that is about to be canceleld
- order
- The instance of satchmo_store.shop.models.Order that is about to be cencelled (Note: this argument is the same as sender)
Sent when an order has been cancelled (it’s status already reflects it and has been saved to the database).
Arguments sent with this signal:
- sender
- The instance of satchmo_store.shop.models.Order that was cancelled
- order
- The instance of satchmo_store.shop.models.Order that was cancelled (Note: this argument is the same as sender)
Sent after an item has been successfully added to the cart.
Arguments sent with this signal:
- sender
- The instance of satchmo_store.shop.models.Cart that was added to
- cart
- The instance of satchmo_store.shop.models.Cart that was added to (Note: this argument is the same as sender)
- added_item
- The instance of satchmo_store.shop.models.CartItem that was added (or updated) to the cart
- product
- The instance of product.models.Product that was added to the cart
- request
- The HttpRequest object used in the view to add the item to the cart
- form
- The POST data for the form used to add the item to the cart
Sent before an item is added to the cart.
Arguments sent with this signal:
- sender
- The instance of satchmo_store.shop.models.Cart that is having an item added to it
- cart
- The instance of satchmo_store.shop.models.Cart that is having an item added to it (Note: this argument is the same as sender)
- cartitem
- An instance of satchmo_store.shop.models.CartItem that is being added to the cart
- added_quantity
- The number of cartitem items being added to the cart
- details
A list of dictionaries containing additonal details about the item if the item is a custom product or a gift certificate product. Each dictionaries will contain the following information:
- name
- The name of the detail
- value
- The value of the detail
- sort_order
- The order the detail should be listed in displays
- price_change
- The price change of the detail, default of zero
Sent whenever the status of the cart has changed. For example, when an item is added, removed, or had it’s quantity updated.
Arguments sent with this signal:
- sender
- The instance of satchmo_store.shop.models.Cart that was changed
- cart
- The instance of satchmo_store.shop.models.Cart that was changed (Note: this argument is the same as sender)
- request
- The HttpRequest object used in the view to change the cart
Sent by the pricing system to allow price overrides when displaying line item prices.
Arguments sent with this signal:
- sender
- The instance of satchmo_store.shop.models.CartItem being querried for price overrides.
- cartitem
- The instance of satchmo_store.shop.models.CartItem being querried for price overrides (Note: this argument is the same as sender)
Sent before an item is added to the cart so that listeners can update product details.
Arguments sent with this signal:
- sender
- The instance of satchmo_store.shop.models.Cart being added to
- product
- Either an instance of product.models.Product or product.models.ConfigurableProduct being added to the cart
- quantity
- The number of items (represented as a Decimal) being added to the cart
- details
A list of dictionaries containing additonal details about the item if the item is a custom product or a gift certificate product. Each dictionaries will contain the following information:
- name
- The name of the detail
- value
- The value of the detail
- sort_order
- The order the detail should be listed in displays
- price_change
- The price change of the detail, default of zero
- request
- The HttpRequest object used in the view to add an item to the cart
- form
- The POST data for the form used to add the item to the cart
Sent after each item from the cart is copied into an order
Arguments sent with this signal:
- sender
- The instance of satchmo_store.shop.models.Cart being copied
- cartitem
- The instance of satchmo_store.shop.models.CartItem being copied into an order
- order
- The instance of satchmo_store.shop.models.Order having items copied into it
- orderitem
- The instance of satchmo_store.shop.models.OrderItem being added to the order
Sent by search_view to ask all listeners to add search results.
Arguments sent with this signal:
- sender
- The product.models.Product model (Note: not an instance of Product)
- request
- The HttpRequest object used in the search view
- category
- The category slug to limit a search to a specific category
- keywords
- A list of keywords search for
- results
A dictionary of results to update with search results. The contents of the dictionary should contain the following information:
- categories
- A QuerySet of product.models.Cateogry objects which matched the search criteria
- products
- A Queryset of product.models.Product objects which matched the search critera
Sent after the satchmo_store.shop.context_processors.settings has been run, but before the context is returned. This signal can be used to modify the context returned by the context processor.
Arguments sent with this signal:
- sender
- The satchmo_store.shop.models.Config instance for the current store configuration
- context
A dictionary containing the context to be returned by the context processor. The dictionary contains:
- shop_base
- The base URL for the store
- shop
- An instance of satchmo_store.shop.models.Config representing the current store configuration
- shop_name
- The shop name
- media_url
- The current media url, taking into account SSL
- cart_count
- The number of items in the cart
- cart
- An instance of satchmo_store.shop.models.Cart representing the current cart
- categories
- A QuerySet of all the product.models.Category objects for the current site.
- is_secure
- A boolean representing weather or not SSL is enabled
- request
- The HttpRequest object passed into the context processor
- login_url
- The login url defined in settings.LOGIN_URL
- logout_url
- The logout url defined in settings.LOGOUT_URL
- sale
- An instance of product.models.Discount if there is a current sale, or None
Sent by satchmo_store.shop.views.smart.smart_add to allow listeners to optionally change the responding function
Arguments sent with this signal:
- sender
- The satchmo_store.shop.models.Cart model (Note: this is not an instance)
- request
- The HttpRequest object used by the view
- method
A dictionary containing a single key view to be updated with the function to be called by smart_add. For example:
method = {'view': cart.add }
Sent when contact information is viewed or updated before a template is rendered. Allows you to override the contact information and context passed to the templates used.
Arguments sent with this signal:
- sender
- An instance of satchmo_store.contact.models.Contact representing the contact information being viewed, or None if the information cannot be found.
- contact
- An instance of satchmo_store.contact.models.Contact representing the contact information being viewed, or None if the information cannot be found (Note: This argument is the same as sender).
- contact_dict
- A dictionary containing the intitial data for the instance of satchmo_store.contact.forms.ExtendedContactInfoForm instance that will be rendered to the user.
Sent after a user changes their location in their profile
Arguments sent with this signal:
- sender
- The instance of satchmo_store.contact.forms.ContactInfoForm which was responsible for the location change.
- contact
- The instance of satchmo_store.contact.models.Contact which was updated with a new location.
Sent when a contact info form is initialized. Contact info forms include:
- contact.forms.ContactInfoForm
- contact.forms.ExtendedContactInfoForm
- payment.forms.PaymentContactInfoForm
Arguments sent with this signal:
- sender
- The model of the form being initialized. The value of sender will be one of the models defined above.
- form
- An instance of the form (whose type is defined by sender) being intitialized.
Sent after a form has been saved to the database
Arguments sent with this signal:
- sender
The form model of the form being set (Note: Not an instance). Possible values include:
- satchmo_store.contact.forms.ContactInfoForm
- payment.modules.purchaseorder.forms.PurchaseorderPayShipForm
- payment.forms.CreditPayShipForm
- payment.forms.SimplePayShipForm
- payment.forms.PaymentContactInfoForm
- form
- The instance of the form defined by one of the above models that was saved.
Sent when a form that contains postal codes (shipping and billing forms) needs to validate. This signal can be used to custom validate postal postal codes. Any listener should return the validated postal code or raise an exception for an invalid postal code.
Arguments sent with this signal:
- sender
- An instance of satchmo_store.contact.forms.ContactInfoForm which is validating it’s postal codes.
- postcode
- The postal code as a string being validated
- country
- An instance of l10n.models.Country that was selected in the form (or specified in the configuration if local sales are only allowed)
Sent after a user has registered an account with the store.
Arguments sent with this signal:
- sender
- The instance of satchmo_store.accounts.forms.RegistrationForm which was submitted.
- contact
- The instance of satchmo_store.contact.models.Contact that was saved to the database
- subscribed
- A boolean reflecting weather or not the user subscribed to a newsletter (defaults to False)
- data
- The cleaned_data dictionary of the submitted form
Sent after a user account has been verified. This signal is also sent right after an account is created if account verification is disabled.
Arguments sent with this signal:
- sender
- An instance of satchmo_store.models.Contact if the account was verified via email (Note: this is the same argument as contact), or an instance of satchmo_store.accounts.forms.RegistrationForm if account verification is disabled.
- contact
- The instance of satchmo_store.models.Contact that was registered
Sent after a newsletter subscription has been updated
Arguments sent with this signal:
- sender
- An instance of satchmo_store.models.Contact representing the person having the subscription status updated
- old_state
- Boolean representing the old state of subscription
- new_state
- Boolean representing the new state of the subscription
- contact
- An instance of satchmo_store.models.Contact representing the person having the subscription status update (Note: this argument is the same as sender)
- attributes
- An empty dictionary. This argument is not currently used.
Sent after ensuring that the cart and order are valid
Arguments sent with this signal:
- sender
- An instance of payment.views.confirm.ConfirmController which performed the sanity check.
- controller
- An instance of payment.views.confirm.ConfirmController which perofmred the sanity check (Note: this is the same argument as sender)
Sent when a payment form is initialized. Payment forms include:
- payment.forms.PaymentContactInfoForm
- payment.forms.SimplePayShipForm
Arguments sent with this signal:
- sender
- The model of the form being initialized. The value of sender will be one of the models defined above.
- form
- An instance of the form (whose type is defined by sender) being intitialized.
This signal can be combined with the payment.listeners.form_terms_listener to add a custom terms and conditions acceptance box into your checkout flow.
First, add the terms view in your /localsite/urls.py file:
urlpatterns += patterns('',
url(r'^shop_terms/$', 'project-name.localsite.views.shop_terms',
name="shop_terms"),
)
Next, create the view in your /localsite/views.py to display the terms:
from django.shortcuts import render_to_response
from django.template import RequestContext
def shop_terms(request):
ctx = RequestContext(request, {})
return render_to_response('localsite/shop-terms.html',
context_instance=ctx)
Now, you will need modify the checkout html to display the new form. Copy /satchmo/apps/payment/templates/shop/checkout/pay_ship.html to /project-name/templates/shop/checkout/pay_ship.html.
Add the following code to the copied pay_ship.html to display the form:
{{ form.terms }} {{ form.terms.label|safe }}
{% if form.terms.errors %}<br/>**{{ form.terms.errors|join:", " }}{% endif %}
Make sure you register the forms_terms_listener by adding the following code to your /localsite/models.py:
from payment.forms import SimplePayShipForm
from payment.listeners import form_terms_listener
from signals_ahoy.signals import form_init
form_init.connect(form_terms_listener, sender=SimplePayShipForm)
The final step is to create your actual /templates/shop-terms.html, like this:
{% extends "base.html" %}
{% block content %}
<p>Put all of your sample terms here.</p>
{% endblock %}
Now, when users checkout, they must agree to your store’s terms.
Sent when a payment.forms.PaymentMethodForm is initialized. Receivers have cart/order passed in variables to check the contents and modify methods list if neccessary.
Arguments sent with this signal:
- sender
- The model payment.forms.PaymentMethodForm
- methods
- A list of 2-element tuples containing the currently active payment methods. Each tuple contains the payment module name as well as the label for that payment module
- cart
- An instance of satchmo_store.shop.models.Cart representing the current cart
- order
- An instance of satchmo_store.shop.models.Order representing the current order
- contact
- An instance of satchmo_store.contact.models.Contact representing the current customer (if authenticated, so it may be None).
Sent at the end of clean() method of:
- payment.forms.PaymentMethodForm
- payment.forms.PaymentContactInfoForm
Arguments sent with this signal:
- sender
- The model of the form that is finishing validation
- form
- The instance of the form whose type is that of sender
Sent after one of the following forms (or their subtypes) has been saved:
- payment.forms.PaymentContactInfoForm
- payment.forms.SimplePayShipForm
- payment.forms.CreditPayShipForm
- payment.modules.purchaseorder.forms.PurcahseorderPayShipForm
- satchmo_store.contact.forms.ContactInfoForm
Arguments sent with this signal:
- sender
- The model of the form that was just saved
- object
- A satchmo_store.contact.models.Contact instance if the form being saved is an instance of satchmo_store.contact.forms.ContactInfoForm otherwise this value does not exist.
- formdata
- The data associated with the form if the form being saved is an instance of satchmo_store.contact.forms.ContactInfoForm otherwise this value does not exist.
- form
- The instance of the form whose type is that of sender
Sent after a list of payment choices is compiled, allows the editing of payment choices.
Arguments sent with this signal:
sender
Always None
- choices
- A list of 2-element tuples containing the currently active payment methods. Each tuple contains the payment module name as well as the label for that payment module
Sent before an index is rendered for categories or brands
Arguments sent with this signal:
- sender
One of the following models:
- product.models.product
- satchmo_ext.brand.models.Brand
- ‘’satchmo_ext.brand.models.BrandProduct``
- request
- The HttpRequest object used by the view
- context
- A dictionary containing the context that will be used to render the template. The contents of this dictionary changes depending on the sender.
- category
- An instance of product.models.Category representing the category being viewed. This paramemter will not exist for brand listings.
- brand
- An instance of satchmo_ext.brand.modes.Brand representing the brand being viewed. This parameter will not existi for category listings.
- object_list
- A QuerySet of product.models.Product objects
Sent before returning the price of a product. The following models send this signal:
- product.models.ProductPriceLookup
- product.models.Price
- satchmo_ext.tieredpricing.models.TieredPrice
Arguments sent with this signal:
- sender
- The instance of the model sending the price query
- price
- The instance of the model sending the price query (Note: This argument is the same as sender)
- slug
- The slug of the product being querried (Note: Only sent with product.models.ProductPriceLookup)
- discountable
- A boolean representing weather or not the product price is discountable (Note: only sent with product.models.ProductPriceLookup)
Sent when a downloadable product is successful
Arguments sent with this signal:
- sender
- The instance of product.models.DownloadableProduct that was successfully ordered.
- product
- The instance of product.models.DownoadableProduct that was successfully ordered.
- order
- The instance of satchmo_store.shop.models.Order that was sucessful
- subtype
- always the string “download”
Sent to verify the set of order items that are subject to discount.
Arguments sent with this signal:
- sender
- The instance of product.models.Discount that is being applied.
- discounted
- A dictionary, where the keys are IDs of items which are subject to discount by standard criteria.
- order
- The instance of satchmo_store.shop.models.Order being processed.
Sent by urls modules to allow listeners to add or replace urls to that module
Arguments sent with this signal:
- sender
- The module having url patterns added to it
- patterns
- The url patterns to be added. This is an instance of django.conf.urls.defaults.patterns
- section
- The name of the section adding the urls (Note: this argument is not always provided). For example ‘__init__’ or ‘product’
Usage:
from satchmo_store.shop.signals import satchmo_cart_add_complete import myviews satchmo_cart_add_complete.connect(myviews.cart_add_listener, sender=None)
This section contains a brief example of how to use signals in your application. For this example, we want to have certain products that are only available to members. Everyone can see the products, but only members can add to the cart. If a non-member tries to purchase a product, they will get a clear error message letting them know they need to be a member.
The first thing to do is create a listeners.py file in your app. In this case, the file would look something like this:
"""
A custom listener that will evaluate whether or not the product being added
to the cart is available to the current user based on their membership.
"""
from satchmo_store.shop.exceptions import CartAddProhibited
from django.utils.translation import gettext_lazy as _
class ContactCannotOrder(CartAddProhibited):
def __init__(self, contact, product, msg):
super(ContactCannotOrder, self).__init__(product, msg)
self.contact = contact
def veto_for_non_members(sender, cartitem=None, added_quantity=0, **kwargs):
from utils import can_user_buy
customer = kwargs['cart'].customer
if can_user_buy(cartitem.product, customer):
return True
else:
msg = _("Only members are allowed to purchase this product.")
raise ContactCannotOrder(customer, cartitem.product, msg)
Next, you need to create the can_user_buy function. Your utils.py file could look something like this (details left up to the reader):
def can_user_buy(product, contact=None):
"""
Given a product and a user, return True if that person can buy it and
False if they can not.
This doesn't work as it stands now. You'll need to customize the
is_member function
"""
if is_member(contact):
return True
else:
return False
The final step is to make sure your new listener is hooked up. In your models.py add the following code:
from listeners import veto_for_non_members
from satchmo_store.shop import signals
signals.satchmo_cart_add_verify.connect(veto_for_non_members, sender=None)
Now, you should be able to restrict certain products to only your members. The nice thing is that you’ve done this without modifying your satchmo base code.