One day I had a conversation with a friend of mine who uses Laravel PHP framework in his job. When I told him that Django has its own all-included HTML CRUD system, for interacting with the database, called Django admin, his eyes popped off! He told me: "It took me months to build an Admin interface for my current web app and you're saying that you have all these without having to write a single line of code?". I responded "Yeap!"
But what "actions" are?
Suppose you have a model and you have already added some entries (maybe hundreds, maybe thousands) into it. Now, you want to apply a rule-action to at least one of them, without using the console (
python manage.py shell):
# python manage.py shell (interactive console) from math import ceil from my_app.models import Product DISCOUNT = 10 # percentage for product in Product.objects.filter(is_active=True): """ Set discount to ALL products that are flagged as active """ multiplier = DISCOUNT / 100. # DISCOUNT / 100 in python 3 (without dot) old_price = product.price new_price = ceil(old_price - (old_price * multiplier)) # seller wins :) product.price = new_price product.save(update_fields=['price'])
Did you noticed that we applied the discount to all products. What if we wanted to apply this logic to specific ones? Or if we wanted to manually enter the value of discount and then apply this value to some products? All these through the Django admin! You are on the right track. Django actions FTW. Lets look at a complete example.
Assuming you have already started a project, go to the directory where
manage.py lives and create an app with the name
stock, by typing:
python manage.py createapp stock
Django will automatically create a directory structure for you. Go and edit
models.py file and add:
# models.py from django.db import models class Product(models.Model): name = models.CharField('Product name', max_length=100) # required field price = models.PositiveIntegerField('Product price') def __str__(self): # __unicode__ in Python 2 return self.name
Product model has been created, but nothing in the database yet. In order for migrations to work, our app must be included in the
settings.py file and under the
INSTALLED_APPS list add:
# settings.py INSTALLED_APPS = [ # ... previous Django apps 'stock.apps.StockConfig', ]
python manage.py makemigrations python manage.py migrate
migrate command, your database has now a table named
product which contains three columns,
price. So far so good!
If you haven't changed anything in your ROOT_URLCONF file, which usually lives inside the folder
<your_project_name>/<your_project_name>/, then the URL that points to the Django admin site should be:
# urls.py urlpatterns = [ # ... other URLs url(r'^admin/', admin.site.urls), ]
Until now, we haven't looked anything specific about the Django admin actions. Take a final step and add these inside your
# admin.py from django.contrib import admin from .models import Product @admin.register(Product) class ProductAdmin(admin.ModelAdmin): pass
OK. The setup is done. Just to make sure everything works, run:
python manage.py runserver
and with your favourite browser visit the page
127.0.0.1:8000/admin/. You should see the shiny-glamorous-terrific Django admin page, which allows you to Create-Read-Update-Delete your
If by any chance, the above page asks you for a username/password and you do not have any, no problem. You just haven't created a
User to login to the admin. Simply run:
python manage.py createsuperuser
enter your name, email, username and password (twice) and you're done.
So far we have created the model but no entries (no products) yet. We need some entries in order to shed light to the power of Django admin actions.
We are going to create 100 products and work with these. But, instead of manually pressing the ADD button and entering a
name and a
price, we will write a script to do the job for us.
python manage.py shell and enter the following:
# python manage.py shell from stock.models import Product for i in range(1, 101): p = Product.objects.create(name='Product %s' % i, price=i)
for loop creates (which means that data are saved in the database) 100 products (entries) with names
Product 2, ...
Product 100 and prices
In order to view these products through the Django admin page, just visit again
127.0.0.1:8000/admin/ and click the
Enjoy your auto-generated 100 products:
This is known as the Django's
change list page. Now, in order for this to look much prettier, edit your
stock/admin.py file and enter:
@admin.register(Product) class ProductAdmin(admin.ModelAdmin): list_display = ('name', 'price')
Now, hit refresh and you should see a second column displaying the prices.
To recap, we have the model and we have the entries. Next, we want to create an action which once selected it will make a discount 30% to the selected product(s).
Did you notice that there is, already, a select box at the top of the
change list page, with the label
Action? Django, automatically adds a default action on each entry which performs the Delete action.
Django admin actions are written as simple functions. Lets dive in. Edit the
stock/admin.py file and add the following:
# admin.py @admin.register(Product) class ProductAdmin(admin.ModelAdmin): list_display = ('name', 'price') actions = ['discount_30'] def discount_30(self, request, queryset): from math import ceil discount = 30 # percentage for product in queryset: """ Set a discount of 30% to selected products """ multiplier = discount / 100. # discount / 100 in python 3 old_price = product.price new_price = ceil(old_price - (old_price * multiplier)) product.price = new_price product.save(update_fields=['price']) discount_30.short_description = 'Set 30%% discount'
A few things to note here:
ProductAdminclass has one extra attribute (
actions) which is a list of strings (each string is the name of the function that represents the action).
ProductAdminclass. It takes as arguments the
selfsince this is a method), the
HTTP requestobject and the
queryset(a list of the selected objects-entries-products).
short_description) which sets the displayed name inside the actions select box (there is a double % there in order to escape the single %).
Inside the function-action we iterate on each product (that was selected) and we set its value decreased by 30%. Then we call the
save() method with the argument
update_fields in order to force an UPDATE on the fields that are included in the
update_fields list (instead of an UPDATE on all the fields of the model) in the database, for performance reasons (not a performance gain in this example, with only 2 columns, but you get the point).
Now, hit refresh in the
change list page and you should see your action under the
delete one. Go ahead and select some products (using the checkbox on the left of each or all products using the left-top most checkbox), select the
Set 30% discount action and click the
Go button. That's it!
Of course, this is not very handy in most situations, since this action does not allow you to enter a different amount of discount. You must edit the
admin.py file each time you want to apply a different discount. In the upcoming example we will see how to do that.