How to start programming in Python on Windows

To develop in Django can be confusing for a new Python developer but using Windows to develop in Django can be a major obstacle too.

How to choose the right IDE for Windows and how to find and install Python libraries? Below six fundamental resources to program with Python on Windows.

Bitnami Django Stack

For developer using Windows, Bitnami Django Stack is a life-saver. It raises you to the need of installing and configuring many libraries and simply create a Python / Django environment on your system. Even if you don’t want to use Django, it can be a great starting point to install Python and fundamental libraries you can extend via PyCharm.

PyCharm

complexlook2x

Screenshot: official website

JetBrains’ PyCharm is the multiplatform IDE to develop in Python. You can forget about the indentation issue and focus on programming. The autocomplete dropdown, the Python console, the easy management of DVCS systems (Git, Mercurial), the easy access to Python packages repositories will make it the tools for Python programming, especially in Windows where there are few alternatives than Linux. On Windows, rely on the Bitnami Django Stack you’re using to load the right libraries.

PyPI – Cheese Shop

PyPI is the repository of Python packages. Since the PyPI is nearly unpronounceable, you can call it Cheese Shop. Python was named by Guido van Rossum after the British comedy group Monty Python and the Cheese Shop is this sketch:

Contrary on the poor guy in the sketch, you will find all sort of cheese you need in the cheese shop.

Pip

Pip is the definitive tool for installing Python packages from Cheese shop on your environment. pip install package-name and you’ll get the package ready and running. Even more interesting is the pip install -r requirements.txt feature. It will install all the packages listed in the requirements.txt text file usually shipped with a package having some dependencies.

PgAdmin

pgadmin4-properties.png

Screenshot: official website

Django and PostgreSQL DBMS are a powerful couple. If you have to use a PostgreSQL database, the best interface you can use is PgAdmin.

Django Packages

Django Packages is the Hitchhiker guide to the cheese shop. You’ve to choose a REST framework but you don’t want to marry with a unreliable partner? You need a good photo gallery and you want to get the best django app to implement in your django application? Django packages will guide you to the best solution for your needs.

django-packages

Any feature has a comparison matrix, where all projects are listed in columns where these criterion, elaborated from Github, are contemplated:

  • Project status (production, beta, alpha)
  • Commit frequency in the repository
  • How many times the project was forked
  • Who work on the project
  • Link to online documentation
  • Features comparison

If you’re coming from a CMS like Drupal here some tips to how to approach a Model-View-Controller like Django, starting from the Entity-Relationship model.

Personal note: Back in the 1998 I start to develop application for the web using ASP and PHP and dependencies weren’t an issue since these languages are for the web. Developing in Python is more challenging and really more fun than programming in PHP. You have a powerful multipurpose language with a ton of libraries competing in a far larger arena than the web development. Not surprising, Google use this language extensively as of some popular web services like Pinterest and Instagram: these last two are using Django.

Read also on the same topic: Django development on Virtualbox: step by step setup

Advertisements

From Drupal to Django: how to migrate contents

In a recent article¬†I explain the motivations for an upgrade from a no longer maintained Drupal 6 installation to¬†Django 1.8. I will now cover more in detail the migration techniques adopted in the upgrade and I’ll deepen the models and the relationships.

Structure

If you’re a drupaler, you’re familiar with the node/NID/edit and the node/add/TYPE pages:

A-New-Page-Drupal-6-Sandbox

Here we have two visible fields: Title and Body. One is an input type text and the other a texarea. The good Form API provided by Drupal calls these two types textfield and textarea. However if you use the Content type creation interface you don’t see any of these, just declare some field types and you’ll see the form populating with new fields after the addition.

It’s similar in Django but you haven’t to pass to a graphical interface to do this: structure is code-driven and the side effect is the ability to put on revision almost anything. You can choose between different field types that will be reflected in database and on the user interface.

Here what the Drupal Body and Title fields looks like in a model called Article:

# models.py
from django.db import models
from tinymce import models as tinymce_models
# Articles
class Article(models.Model):
    title       = models.CharField(max_length=250,null=False, blank=False)
    body        = tinymce_models.HTMLField(blank=True, default='')

The TinyMCE part require TinyMCE app installed and configured.¬†If you’re new to Django read and follow the great Writing your first Django app¬†to understand the basics, e.g the difference between a project and an app or the following sections will sound pretty obscure.

After editing your projectname/appname/models.py file you can now apply the changes in your app via makemigrations (create a migration file for the changes in the database) and migrate (apply the migrations inside the migration files).

In a real world scenario these two fields alone aren’t enough neither in a Drupal 6.¬†These information are all presented by default in any type on Drupal 6:

authoring-info

Drupal 6 treats author as entities you can search through an autocomplete field, and date as a pseudo-ISO 8601 date field. The author field is a link to the User table in Drupal. In Django a similar user model¬†exists but if you want to unchain the access to the admin backend and the authorship it’s simpler to¬†create a custom author model and later associate this with the real user model.

sport3_uml

E-R of our app where migrate the Drupal contents to.

from django.db import models
from tinymce import models as tinymce_models
# Authors
class Author(models.Model):
    alias       = models.CharField(max_length=100)
    name        = models.CharField(max_length=100, null=True, blank=True)
    surname     = models.CharField(max_length=100, null=True, blank=True)
# Articles
class Article(models.Model):
    author      = models.ForeignKey('Author', verbose_name='Authored by')
    title       = models.CharField(max_length=250,null=False, blank=False)
    body        = tinymce_models.HTMLField(blank=True, default='')
    publishing_date = models.DateTimeField(auto_now=False, auto_now_add=False, verbose_name='First published on')

As you can see in the Entity-Relationship diagram one¬†Article must have one and only one Author, but many¬†Articles can have the same Author. This is called Many-to-one relationship¬†and it’s represented in Django as a foreign key from the destination “many” model (e.g. Article) to the “one” model (Author).

The Article.publishing_date field is where publishing date and time are stored and, clicking on the text field, a calendar popup is presented to choose the day and hour, with a useful “now” shortcut to populate the field with the current time.

calendario

How a calendar is represented in a DateTime field.

Now that the basic fields are in the right place you can makemigrations / migrate again to update your app, restarting the webserver to apply the changes.

Attachments and images

Drupal is shipped with the ability to upload files and images to nodes. Django has two different field for this: FileField and ImageField. Before continuing we have to rethink our E-R model to allow attachments.

sport3_uml

 

The model.py code is:

from django.db import models
from tinymce import models as tinymce_models
# Authors
class Author(models.Model):
    alias       = models.CharField(max_length=100)
    name        = models.CharField(max_length=100, null=True, blank=True)
    surname     = models.CharField(max_length=100, null=True, blank=True)
# Articles
class Article(models.Model):
    author      = models.ForeignKey('Author', verbose_name='Authored by')
    title       = models.CharField(max_length=250,null=False, blank=False)
    body        = tinymce_models.HTMLField(blank=True, default='')
    publishing_date = models.DateTimeField(auto_now=False, auto_now_add=False, verbose_name='First published on')
# Attachments
class Attachments(models.Model):
    description = models.CharField(max_length=255, default='', blank=True)
    list = models.BooleanField(default=True)
    file = models.FileField(upload_to='attachments_directory', max_length=255)

Images are similar: if you want to enrich your model with images you can create another model like Attachments but with an ImageField instead. Remember to use a different upload_to directory in order to keep the attachments and images separated.

We miss the last one field to complete our models: path. Django comes with an useful SlugField that as of Django 1.8 allows only ASCII characters and can be mapped to another field, the title for example.

from django.db import models
from tinymce import models as tinymce_models
# Articles
class Article(models.Model):
    author      = models.ForeignKey('Author', verbose_name='Authored by')
    title       = models.CharField(max_length=250,null=False, blank=False)
    body        = tinymce_models.HTMLField(blank=True, default='')
    publishing_date = models.DateTimeField(auto_now=False, auto_now_add=False, verbose_name='First published on')

Keep in mind that a SlugField differs from a Drupal path field because it doesn’t allow slashes. Consider a path like this:

news/news-title

In Drupal you will have a A) view with the path news and the argument news title or B) a fake path generated by pathauto or similar modules. In years of Drupal development, I can affirm that the B option is the typical easy way that turns into a nightmare of maintainance. Django core as far as I know allows only the A choice, so if you want a news view you have to declare it in urls.py and then in views.py as stated in official documentation.

  • news/: the news root path, coupled with the view
  • news-title: the ¬†argument passed to the view and¬†the SlugField content for an article. It must be unique to be used as key to retreive an article but since it can be empty we cannot force it to has a value or to be unique at first. When all data are imported and fixed we can change this field to unique to improve database retrieval performance.

Categories

And what about categories? If you have a category named Section, and an article can be associated with only one Section, you have to create a Many-to-one relationship. As you see before, you have to put the foreign key in the N side of the relation, in this case Article, so the model Article will have a ForeignKey field referencing a specific section.

On the other hands if you have tags to associate to your article you have to create a Tag model with a Many-to-many relationship to the Article. Django will create an intermediate model storing the Article-Tag relationships.

Do not abuse of M2M relationships because each relation needs a separate table and the number of JOIN on database table will increase with side effects on the performance, not even perceivable on the first since Django ORM is very efficient. The event handling will be more difficult for a beginner since the many to many events occurs only when the parent models are saved and this require some experience if you need to add a custom action to a M2M event. If you design wisely your E-R model you have nothing to be scared of.

Migration techniques

Now that we have the destination models, fields and relationship we can import the content from Drupal. In the previous article I suggested to use Views Datasource module to create a JSON view to export content. Please read the Exporting the data from Drupal section inside the article before continue.

The obtained row is something like:

{
  [
    {
      {nid: '30004',
      domainsourceid: '2',
      nodepath: 'http://example.com/path/here',
      postdate: '2014-09-17T22:18:42+0200',
      nodebody: 'HTML TEXT HERE',
      nodetype: 'drupal type',
      nodetitle: 'Title here',
      nodeauthor: 'monty',
      nodetags: 'Drupal, dragonball, paintball'
      }
    },
    ...
  ]
}

If you haven’t a multi-site Drupal you can ignore domainsourceid field.¬†The nodetags lists some Tag names of a Many-to-many relationship not covered here.

All the other value are useful for the import:

  • nid: the original content id, used for pagination and retrieval
    Destination: parsing
  • nodepath:¬†content path
    Destination: Article.path
  • nodebody: content body
    Destination: Article.body
  • nodetype: type of the node
    Destination: parsing
  • nodetitle: title of the node
    Destination: Article.title
  • nodeauthor:¬†author of the content
    Destination: Article.author -> Author.alias

In the previous article you find how to make the View on Drupal (source) and now you have  rough idea of the field mapping. How to fetch the data from Django?

Management command and paged view

To start a one-time import you can write a custom management command for your Django application named project/app/management/commands/myimport.py.

from __future__ import unicode_literals
from django.core.management.base import BaseCommand, CommandError
from django.core.exceptions import ValidationError, MultipleObjectsReturned, ObjectDoesNotExist
import json, urllib
import urlparse
from shutil import copyfile
from django.conf import settings
from os import sep
from django.core.files.storage import default_storage
from django.utils.text import slugify
import requests
import grequests
import time
from md5 import md5

class Command(BaseCommand):
    help = 'Import data from Drupal 6 Json view'
    def add_arguments(self, parser):
        parser.add_argument('start', nargs=1, type=int)
        parser.add_argument('importtype', nargs=1)
        # Named (optional) arguments
        # Crawl
        parser.add_argument('--crawl',
            action='store_true',
            dest='crawl',
            default=False,
            help='Crawl data.')
    def handle(self, *args, **options):
        # process data
        pass

This management command can be launched with

python manage.py myimport 0 article --crawl

Where 0 is the item to start + 1, “article” is the type of content to import (e.g. the destination model) and –crawl is the import option. Let’s add the import logic to the Command.handle method:

def handle(self, *args, **options):
    try:
        assert options['crawl'] and options['importtype']
        # start to import or store data
        sid = int(options['start'].pop())
        reading = True
        while reading:
            importazioni = []
            articoli = []
            url = 'http://www.example.com/json-path-verylongkey?nid=%d' % (sid,)
            print url
            response = urllib.urlopen(url)
            data = json.loads(response.read())
            data = data['']
            # no data received, quit
            if not data:
                reading = False
                break
            for n, record in enumerate(data):
                sid = int(record['']['nid'])
                title = record['']['nodetitle']
                # continue to process data, row after row
                # ...

    except AssertionError:
        raise CommandError('Invalid import command')

This example will fetch /json-path-verylongkey starting from nid passed from the command + 1. Then, it will process the json row after row and keep in memory the id of the last item. When no content is available, the cycle will stop. It’s a common method and it’s lightweight on the source server because only one request at time are sent and then the response is processed. Anyway, this method can be also slow because we have to sum waiting time: (request 1 + response 1 + parse 1) + (request 2 + response 2 + parse 2) etc.

Multiple, asyncronous requests

We can speed up the retrieval by using grequests. You have to check what is the last element first by cloning the Drupal data source json view and showing only the last item, then fetching the id.

def handle(self, *args, **options):
    try:
        assert options['crawl'] and options['importtype']
        # start to import or store data
        sid = int(options['start'].pop())
        # find last node id to create an url list
        url = 'http://www.example.com/json-path-verylongkey-last-nid'
        response = requests.get(url, timeout = 50)
        r = response.json()
        last_nid = int(r[''].pop()['']['nid'])

You can then create a from-to range starting from the first element passed by command line to the last.

url_pattern = "http://www.example.com/json-path-verylongkey-last-nid?fromnid=%d&tonid=%d";
urls = []
per_page = 20
# e.g. [0, 20, 40, 60]
relements       = range(0, last_nid, per_page)
if relements[-1] < last_nid:
    relements.append(last_nid + 1)
for fromx, toy in zip(relements, relements[1:]):
    u = url_pattern % (fromx, toy)
    urls.append(u)

rs = (grequests.get(u) for u in self.urls)
# blocking request: stay here until the last response is received
async_responses = grequests.map(rs)
# all responses fetched

The per_page is the number of element per page specified on Drupal json view. Instead of a single nid parameter, fromnid and tonid are the parameter “greater than” and “less or equal than” specified in the Drupal view.

The core of the asyncronous, multiple requests is grequests.map(). It take a list of urls and then request them. The response will arrive in random order but the async_responses will be populated by all of them.

At that point you can treat the response list like before, parsing the response.json() of each element of the list.

With these hints you can now create JSON views within Drupal ready to be fetched and parsed in Django. In a next article I will cover the conversion between the data and Django using the Django ORM.

Django and Drupal integration using drush via SSH

Some months ago I talked about how to achieve a unified login from Django to Drupal using drush. The basic assumption was that both Drupal and Django are on the same server. What if the two components are on different servers?

Paramiko is a SSH2 protocol library aimed to provide simple classes to make SSH connection. Let’s see how the code to call drush on command line changes.

Prerequisites:

  • paramiko
  • on your app settings.py add:
  • DRUPAL_SERVER_SSH_HOST     = '0.0.0.0' # Your host here
    DRUPAL_SERVER_SSH_USERNAME = 'YourRemoteServerUserHere'
    DRUPAL_SERVER_SSH_PASSWORD = 'YourRemoteServerPasswordHere'

    And then:

    assert request.user.drupal_id > 0
    # user id to log in
    drupal_id = str(request.user.drupal_id)
    output = ""
    try:
     # a list with command as first element and arguments following
     get_password_recovery_url = ["drush", "-r", settings.DRUPAL_SITE_PATH, "-l", settings.DRUPAL_SITE_NAME, "user-login", drupal_id]
     # via ssh http://stackoverflow.com/a/3586168/892951
     ssh = paramiko.SSHClient()
     # add to known_host the remote server key if it's not already stored
     # @see http://jessenoller.com/blog/2009/02/05/ssh-programming-with-paramiko-completely-different
     ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
     ssh.connect(settings.DRUPAL_SERVER_SSH_HOST, username=settings.DRUPAL_SERVER_SSH_USERNAME, password=settings.DRUPAL_SERVER_SSH_PASSWORD)
     ssh_stdin, output, ssh_stderr = ssh.exec_command(" ".join(get_password_recovery_url))
     output_lines = output.read().splitlines()
     # taking only the first line of the output:
     # e.g. 'http://example.com.it/user/reset/16/1369986816/67k7ReHi97FdtRfdrrXGqqesyz6FXyy7T8jqHiXxsrY/login'
    except:
     # @todo additional statements here
     pass
    finally:
     if ssh:
      ssh.close()
    
    if output_lines:
    drupal_login_url = output_lines[0].replace("http://example.com/", "http://%s/" % settings.DRUPAL_SITE_URL).strip()
    
    destination = "%s?destination=%s" % (drupal_login_url, settings.DRUPAL_LOGIN_DESTINATION)
     return redirect(destination)
    else:
     return HttpResponse('
    <h1>Wrong request</h1>
    ')
    

    This is the same code of the previous howto, with the difference that drush now is running on a different server of django. You can use the same method to do anything you have to with drush, any time you call this piece of code an SSH connection is opened.

    See also:

Create nice unicode PDF using Python

Today I started one of the less motivating activities in Python 2.x: encoding.

In Python 3 unicode will be everywhere, but as of the 2.6 version I’ve on one of the server I have to endure.

Objective: get data from a UTF-8 encoded json and print a nice PDF.

Tools: json, urllib2, fpdf, cgi

What you need:
pyfpdf: https://code.google.com/p/pyfpdf/downloads/list

  • Download¬†fpdf-1.7.hg.zip¬†or more recent
  • Unzip, enter the directory and python setup.py install
  • locate fpdf
  • cd¬†/usr/lib/python2.6/site-packages/fpdf (or the directory name you got with locate)
  • Download unicode fonts for fpdf
  • Unzip and copy the fonts folder in the fpdf directory

Now you have a working FPDF with unicode support and unicode fonts. Start to write your script, I assume you’re using python 2.6, if not change python2.6 to your python version (e.g. 2.7) or remove version number in the heading (just python). As now FPDF works with Python 2.5 to 2.7.

Here I write a simple cgi-bin script, so you have to put it in the /var/www/cgi-bin directory (CentOS) or in /usr/lib/cgi-bin (Debian).

#!/usr/bin/env python2.6
#-*- coding: utf-8 -*-
from fpdf import FPDF
import json
import urllib2
import os
import cgi
import sys
# set system encoding to unicode
import sys
reload(sys)
sys.setdefaultencoding("utf-8")

Now get some arguments from url. These will be used to compile a query to a external json service.

# e.g. http://example.com/cgi-bin/myscript.py?lang=en&sid=2
sid = arguments.getlist('sid')[0]
lang = arguments.getlist('lang')[0]
# compile a request to get a particular element from an external json
dataurl = "http://example.com/external-json-source?lang=%s&sid=%s" % (lang, sid)
# load json from dataurl and convert into python elements
data = json.load(urllib2.urlopen(dataurl))
# the json has a user attribute: the user attribute has name and surname attributes as strings
user = data['user']
# title is a simple string
title = data['title']

Now you have to load the json from the external source. Json must be encoded in UTF-8:

lato_lungo = 297
lato_corto = 210
pdf = FPDF('L','mm','A4')
# add unicode font
pdf.add_font('DejaVu','','DejaVuSansCondensed.ttf',uni=True)
pdf.add_page()
pdf.cell(w=lato_lungo,h=9,txt=title,border=0,ln=1,align='L',fill=0)
pdf.set_font('DejaVu','',12)
# paragraphs rendered as MultiCell
# @see https://code.google.com/p/pyfpdf/wiki/MultiCell
# print key: values for each user['data'] dictionary attributes
for val in user.iteritems():
    pdf.multi_cell(w=0,h=5,txt="%s: %s" % val)
# finally print pdf
print pdf.output(dest='S')

Now:

  1. Open your browser and visit http://example.com/cgi-bin/myscript.py?lang=en&sid=2
  2. The external source http://example.com/external-json-source?lang=en&sid=2 is grabbed and converted into a python data structure. Both source and destination encoding are unicode utf-8.
  3. Data from external source are used to create the pdf.

You can use as many fonts as you have in the fpdf/font directory, just add those using pdf.add_font().

https://code.google.com/p/pyfpdf/downloads/list

Scrapy on Debian 6

Debian 6 comes with Scrapy 0.8 as downloadable packages on apt. Here a quick howto to get this spider works on Debian 6.

  1. sudo apt-get install python-scrapy
  2. cd
  3. mkdir mydir
  4. cd mydir
  5. scrapy-ctl startproject anime
  6. export SCRAPY_SETTINGS_MODULE=anime.settings
  7. export PYTHONPATH=/home/YOURHOMEHERE/mydir

If you’ve already a bot but you, to run your spider thanks to point 6 and 7 you can simply type:

scrapy-ctl crawl example.com

Otherwise, now you can follow the howto on tutorial section of Scrapy 0.8 or this awesome howto by Pravin Paratey to write your own bot, but remember to use the scrapy-ctl command instead of the .py version and to add all your spiders to SCRAPY_SETTINGS_MODULE and PYTHONPATH.

To list your available (and correctly configured) spider, just type:

scrapy-ctl list

If a bot doesn’t appear here, you have an issue on point 6 or 7 or you have a misconfigured spider, i.e. I was forgetting the SPIDER part on bottom of my spider and I was using domain instead of domain_name on my script, see Pravin’s howto to write correct Scrapy 8.0 code.

Django development on Virtualbox: step by step setup

I had a bad morning trying to repair my Cygwin installation from a virtualenv mess. It’s time to get a Debian and install it on a Virtualbox for my new django project!

  • Windows: host
  • Debian: guest

Choosing the distro: what I want

  • Python 2.6
  • Django 1.4
  • Apache + Mysql

I’m a Debian fan from years so I go to the Debian website and download Wheezy netinst¬†iso (32 bit, since I’m on a 32 bit OS and I want to use more core): wheezy met all the requirements above.

I already have a Virtualbox, so what I do is to add a new virtual disk and to add the new Wheezy netinst iso on CD/DVD images. Then I create a new Debian machine (32 bit) with two cores. I choose the iso image to be mounted on startup so the Debian setup process will start on boot.

As network device, I choose the Bridge option, so I can access the machine later from my windows host.

Installing the system

When you turn your machine on, many choices will be prompted to you. I install the webserver (apache) from the list, removed SQL server and print server and then leave desktop selected and the other default values. After some minutes Debian is installed and I can log in with the credential I have specified during installation.

Use WORKGROUP as network name if you’re running a Windows host when asked.

Install django packages

Under the Application menu, find the Debian package management tools¬†to install what you want. As the requirements I’ve listeded above I search and install those packages:

  • python-django (1.4.1-2)
  • libapache2-mod-uwsgi
  • libapache2-mod-wsgi
  • mysql-server
  • samba

Later you can install more useful packages like virtualenv and phpmyadmin.

After you’ve installed those packages, you can do some test.¬†Open a shell (Accessories > Terminal) and then type these commands:

What version of python I’m running?

$ python

Python 2.7.3rc2 (default, Apr 22 2012, 22:35:38)
[GCC 4.6.3] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.
>>>

So I’ve python 2.7. Good!

>>> import django
>>> django.VERSION
(1, 4, 1, ‘final’, 0)

And I’ve Django 1.4.1.

Share your code to the Windows network (workgroup)

Now I want to read the code from one machine to another. I choose Samba server to read and write files from the virtual machine to windows and back. It will be useful since I’ve a complete Eclipse + pydev¬†IDE on windows and I love work with it.

I open a Root terminal and type:

# ifconfig

If you choosed the Bridge network interface on installation, you will got something like this:

eth0 Link encap:Ethernet HWaddr ??????
inet addr:192.168.0.104 Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: ???????????/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:49280 errors:0 dropped:0 overruns:0 frame:0
TX packets:19777 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:45400047 (43.2 MiB) TX bytes:1887849 (1.8 MiB)
Interrupt:19 Base address:0xd020

The address in bold (192.168.0.104) is the local network address of my virtual machine. If I just type this address in the Chrome browser it’s running on Windows (host) I got the “It works!” from Apache on the virtual machine. If you can’t see nothing, left click on the network icon on the bottom of your virtualbox windows > click on the menu voice and then choose the Bridge option. Then redo the ifconfig as above.

Samba tuning

Create a directory to store your django code (inside current user home folder). Open the terminal as normal user:

$ cd
$ mkdir my-django-code

Then share this folder with samba. To do this, let’s create a new user without a password:

adduser guest –home=/home/public –shell=/bin/false –disabled-password

Then add these lines to /etc/samba/smb.conf on “## Authentication ##” section:

security = share
guest_account = guest
invalid_users = root

obey pam restrictions = yes

And then after the [cdrom] commented text:

[my-django-code]
comment=Django-code
read only = no
locking = no
path = /home/myuser/my-django-code
guest ok = yes
force user = myuser

Where myuser is my (normal) user name. The lines above tell something like this to samba:

  • Let a guest user access without a password
  • …to the path¬†/home/myuser/my-django-code
  • …”masquerading” like myuser

The “masquerade” thing is all about having the right to write files created from myuser from the guest user on the host.

When i browse my Workgroup on windows, I found the machine name I choose during installation and inside I found the my-django-code directory. I try to read and write files from the host (Windows) and from the guest (Debian) and it’s all ok.

Django, finally!

If you’re starting to develop on django, so this howto for beginners will help you a lot. Since I’ve installed the python-django package from Debian, to start a project is simple as typing this:

$ cd
$ cd my-django-code
$ django-admin startproject django_unchained
$ cd django_unchained
$ python manage.py runserver 192.168.0.104:8000

Where 192.168.0.104 is the virtual machine local network address from above and 8000 the port of the django testing webserver.

I type:

http://192.168.0.104:8000

on Chrome (host: Windows) and I get the hello page from Django. Perfect!

Then, I can just follow the django howto to do the right things during the creation of my new app django_unchained!

You can also explore the must-have list of tools and sites for Python developers.