From Symfony to Django

Lately I’ve been playing with Django (in a real project, not tutorials) and all I have to say is… Oh My God!! I’m so impressed with Django’s clarity, speed of development and “cleanliness”, that I’m even planning for it to become my main ‘go-to’ framework! Don’t get me wrong, I’ve been working with Symfony for a few years and it’s still going to be the main framework I use for PHP projects, and probably the most comfortable I’ll feel working with for a while, but right now Django is like a new toy and I’m really excited to share this with you!

Furthermore, Django seems to perform far better in performance tests (less CPU resources, faster response…) than Symfony. Check here for more info on this.

This blog post is not a ‘Django getting started guide’, there’s plenty of those online already. Please visit Django’s oficial page for more info on this.

This blog post is for Symfony developers who wish to try Django out and are unaware of the Django project structure, file structure, etc. I’m going to go through the basics so you get a feel of what it’s like to code in Django and hopefully you feel comfortable enough to give it a go 🙂

Routing

Routing files are the ones which have mapped the routes for our application. Symfony and Django alike will try to match one of these routes to handle it.

Symfony annotations in controller

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

/**
 * Displays a form to edit an existing customer entity.
 *
 * @Route("/{id}/edit", name="app_customer_edit")
 */

Symfony YAML: routing.yml

app_customer_edit:
    path:     /{id}/edit
    defaults: { _controller: AppBundle:Customer:edit }

In Symfony you can include specific routing of a bundle like so:

app:
    resource:   "@AppBundle/Resources/config/routing.yml"
    prefix:     /

Django: urls.py

Django routing in a config file called urls.py

from app.views import customer

urlpatterns = [
    url(r'^(?P\d+)/edit$', customer.update_customer, name='customer_edit'),
]

You can also include specific routing in Django

urlpatterns = [
    url(r'^customer/', include('app.urls.customer')),
]

Controllers

Once matched a certain route, Controllers are the ones that receive the request and return a response, with whatever logic you may need in between, using services and so on.

Symfony: CustomerController.php

A Controller in Symfony, which has the action that will fire if the route matches

/**
 * Customer controller.
 *
 * @Route("customer")
 */
class CustomerController extends CoreController
{

    /**
     * Displays a form to edit an existing customer entity.
     *
     * @Route("/{id}/edit", name="app_customer_edit")
     */
    public function editAction(Request $request, Customer $customer)
    {
    
        //custom logic
    
        return $this->render('AppBundle:Customer:edit.html.twig', array(
                'param1' => $param1,
                //...
            ));
    }

}

Django: views.py

In Django, the Controller is called View, so the views.py will have the function we specified in the urls.py

def update_customer(request, pk):

    //custom logic

    return render(request, 'customer/edit.html', {
        'param1': param1,
    })

Template rendering

The templating engine is very very similar, so you won’t have any trouble adapting to the new one.

Symfony: edit.html.twig

Twig is the template engine in Symfony. The Resources/views folder in Symfony which contains all the twigs.

For instance, to create an anchor for a Symfony route in twig:

< a href="{{ path('app_customer_edit', { 'id': customer.id }) }}"> Edit the customer < /a>

Django: edit.html

Django uses it’s own templating engine, and the folder containing all the html files is called templates. Code looks very similar.

< a href="{% url "customer_edit" customer.id %}"> Edit the Customer < /a>

Static files

Symfony: public/

The Resources/public folder in Symfony which contains all the js/css files for later deployment.

To load some of those files in the template in Twig

< link rel="stylesheet" href="{{ asset('bundles/app/vendor/bootstrap/css/bootstrap.min.css') }}">
< script type="text/javascript" src="{{ asset('bundles/app/vendor/bootstrap/js/bootstrap.min.js') }}">

Django: static/

In Django, these files are gathered in a folder called static.

{% load static %}

< link rel="stylesheet" href="{% static "vendor/bootstrap/dist/css/bootstrap.min.css" %}">
< script src="{% static "vendor/bootstrap/dist/js/bootstrap.min.js" %}">

Entities

Entities are mapped models that translate into database tables. The idea is to abstract the database completely, so you can either use Mysql, Mongo, PostgreSql,… totally independent from the code.

Symfony: Customer.php

Using Doctrine, the Customer entity under the Entity folder in Symfony would look something like this:

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity()
 * @ORM\Table(name="app_customer")
 */
class Customer
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=50, nullable=true)
     */
    protected $name;

    /**
     * @var string
     *
     * @ORM\Column(name="surname", type="string", length=80, nullable=true)
     */
    protected $surname;

    /**
     * @var Address
     *
     * @ORM\OneToOne(targetEntity="Address", cascade={"persist"})
     * @ORM\JoinColumn(nullable=true)
     */
    protected $address;


    //more attributes...


	/**
     * @return string
     */
    public function __toString()
    {
        return trim((string) $this->getName(). ' '.  $this->getSurname());
    }
    
    /**
     * Get id
     *
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     *
     * @return Customer
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set surname
     *
     * @param string $surname
     *
     * @return Customer
     */
    public function setSurname($surname)
    {
        $this->surname = $surname;

        return $this;
    }

    /**
     * Get surname
     *
     * @return string
     */
    public function getSurname()
    {
        return $this->surname;
    }

    /**
     * Set address
     *
     * @param \AppBundle\Entity\Address $address
     *
     * @return Customer
     */
    public function setAddress(\AppBundle\Entity\Address $address = null)
    {
        $this->address = $address;

        return $this;
    }

    /**
     * Get address
     *
     * @return \AppBundle\Entity\Address
     */
    public function getAddress()
    {
        return $this->address;
    }

    //more getters and setters...

Django: models.py

In Django, much slimmer. No need of any getters and setters, as it has it’s own model API that handles that.

from django.db import models
from app.models.address import Address


class Customer(models.Model):
    name = models.CharField('Name', max_length=50)
    surnames = models.CharField('Surnames', max_length=80)
    address = models.OneToOneField(
        Address,
        on_delete=models.CASCADE
    )

    def __str__(self):
    	return self.name + ' ' + self.surnames

Forms

Symfony: FormType.php

In Symfony we’ll use a FormType, which usually extends from AbstractType. We also need to tell Symfony what class it’s mapping through the configureOptions method.

use Symfony\Component\Form\AbstractType;

class CustomerType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name', null, [
                'label' => 'Nom',
            ])
            ->add('items', CollectionType::class, [
                'entry_type' => CustomerItemType::class,
                'allow_add'    => true,
                'allow_delete' => true,
                'by_reference' => false
            ])
            ...
         ;
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => Customer::class,
            'cascade_validation' => true,
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_customer';
    }
}

To build the form from the controller we would use something like this

$form = $this->createForm(CustomerType::class, $customer);

This basically calls the form builder service and builds an CustomerType form with the $customer instance.

Django: forms.py

In Django it’s much simpler! We also need to tell Django what model class it’s mapping through the Meta assignment.

from django import forms

class CustomerForm(forms.ModelForm):

    class Meta:
        model = Customer
        exclude = ('',)

To build the form from the view we would use something like this

form_customer = CustomerForm(request.POST, instance=customer_inst)

Same as Symfony, this will create the form with the customer_inst instance.

ORM – Object Relational Manager

The ORM allows us to access objects from the database in a ‘object manner’, so we don’t need to worry how to access that DDBB, it’s totally independent.

Symfony: Doctrine

In Symfony we use Doctrine, so let’s find an Customer given a request id

public function editAction(Request $request)
{
	$em = $this->getDoctrine()->getManager();
	$customer = $em->getRepository('AppBundle:Customer')->find($request->get('id'));
	$customers = $em->getRepository('AppBundle:Customer')->findAll();
	$address = $customer->getAddress();

	// Find an customer depending on a address
	$customerInst = $em->getRepository('AppBundle:Customer')->findOneByAddress($addressInst);
}

Django: API

In Django it uses it’s own API.

def update_customer(request, pk):
    customer = Customer.objects.get(pk=your_object_id)
    customers = Customer.objects.all()
    address = customer.address

    # Find an customer depending on a address
    customer_inst = Customer.objects.get(address=address_inst)

Summary

I’m not saying Django is better than Symfony or the other way round. Each and every framework/technology has it’s place, and as developers, I believe we should be open minded and have as many tools as possible under the belt. So I strongly recommend every Symfony dev to try Django out if you haven’t done yet. I assure you that you’ll love everything about it, even the great admin it gives you out of the box 🙂


Also published on Medium.

2 thoughts on “From Symfony to Django”

  1. Hi Joey, this is my first visit to your blog. I appreciate your write-up code comparing the two frameworks, Symfony and Django.

    I’ve been a PHP developer for many years and use CodeIgniter primarily because that’s what my employer uses. Recently, we outsourced a project to a company who built the project with Symfony. It was fun being exposed to a new framework and a new way of doing things. I was tasked with adding a few features to the project which took me a while to implement. Partly because Symfony is new to me and partly because Symfony feels very particular. For instance, I’m sure Symfony devs love annotations but IMHO they feel like an accident waiting to happen.

    Anyway, I find myself wanting to try Python for whatever reason. And based on your code comparisons above, it seems like you get to write less code with Django when compared to Symfony. Awesome!

    While you said you’re not saying one framework is better than the other, you encouraged every Symfony developer to give Django a try. I’m curious if you’d also encourage every Django dev to give Symfony a try. I’d guess no but I could be wrong. 😉

    1. Hi Josh,

      Thank you for your kind comments 🙂

      I’ve been coding with Symfony for over 6 years now, and I still think it’s the best PHP framework after trying other PHP frameworks like CodeIgniter – but that’s just me, my honest opinion – so yes, I would definitely recommend all devs to give it a try if they still haven’t! We all have a go-to technology we feel very comfortable with, and for me that’s Symfony.

      That being said, I feel it’s really important for devs to explore into other languages and frameworks, and Python and Django is definitely one of the best ones I’ve tried so far 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *