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 :)


Originally published at Joey’s blog.

Like what you read? Give Joey Masip Romeu a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.