Coding Style and Conventions
============================

When writing Python code for Misago, please familiarize yourself with and follow those documents:

1. [PEP 8](http://www.python.org/dev/peps/pep-0008/)
2. [Django Coding Style and Conventions](https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/)

Those documents should give you solid knowledge of coding style and conventions that are followed by Python and Django programmers when writing code.

In addition to those guidelines, Misago defines set of additional convetions and good practices that will help you write better and easier code.


##### Notes

Originally Misago was writen with 79 character line limit in mind, but recently this convention was dropped for sake of Django's 119 characters line limit. 

Misago comes with `.pylintrc` file that contains configuration for pylint tool used to lint Misago's codebase.


## Single and double quotes

Write messages intented for humans to see using double quotes and all other strings using single ones. Use single quotes for messages for humans if those already contain double quotes.

```python
# good
message = "Your account has been banned!"
message = 'You cannot use "%(username)s" as username!'

Category.objects.get(role='threads')

# bad
message = 'Your account has been banned!'

Category.objects.get(role="threads")
```


## Models


### Fields Order

When declaring model's database fields, start with taxonomical foreign keys followed by fields that make this model identifiable to humans. Order of remaining fileds is completely up to you.

Thread model is great example of this convention. Thread is part of taxonomy (Forum), so first field defined is foreign key to Forum model. This is followed by two fields that humans will use to recognise this model: `title` that will be displayed in UI and `slug` that will be included in links to this thread. After those Thread model defines additional fields.

When declaring model database fields, make sure they are grouped together based on their purpose. Most common example is storage of user name and slug in addition to foreign key to User model. In such case, make sure both `poster` field as well as `poster_name`, `poster_slug` and `poster_ip` fields are grouped together.


### Avoid Unmeaningful Names

Whenever possible avoid naming fields representing relation to `User` model `user`. Preffer more descriptive names like `poster`, `last_editor`, or `giver`.

For same reason avoid using `date` or `ip` as field names. Use more descriptive `posted_on` or `poster_ip` instead.


### True/False Fields

For extra clarity prefix fields representing true/false states of model with `is_`. `is_deleted` is better than `deleted`.


## Serializers


### Defining serializers

Model's default serializer should be named after its model, but with `Serializer` suffix, ergo serializer for `Thread` model should be named `ThreadSerializer`. Default serializer should be only serializer defining serialization methods for `SerializerMethodField`.

In case that model has more than one serializer, all additional serializers should be named in a way describing their usage. For example serializer for `Post` model used to serialize user's posts list could be named `FeedPostsSerializer`. This serializer should only define `Meta` class with `model` and `fields` attributes, inheriting serialization behaviour form default serializer.


### Fields Order

Order of fields should correspond directly to order of fields on model that serializer handles.

If serializer defines non-model fields, those should be specified last and separated from model's fields with empty line, ergo:

```python
fields = (
    'id',
    'user',
    'title',

    'acl', # annotation set on model by view
    'something_extra', # dynamic atribute coming from "SerializerMethodField"
)
```


### URLs

Serializers may define two special fields used for serialization of url's, `url` and `api`. The first one should be string containing serialized model's `get_absolute_url` or list urls of interest for UI rendering serialized model, like:

```python
'url': {
    'absolute': obj.get_absolute_url(),
    'first_unread': obj.get_first_unread_url(),
    'last_post': obj.get_last_post_url(),
}
```

Likewise the "api" key should contain the url to item api endpoint (eg. `/api/threads/132/`) or list of avaiable endpoints:

```python
'api': {
    'index': reverse('misago:api:threads', kwargs={'pk': obj.pk}),
    'read': reverse('misago:api:thread-read', kwargs={'pk': obj.pk}),
    'move': reverse('misago:api:thread-move', kwargs={'pk': obj.pk}),
}
```

Those keys should live at the end of the fields list and be separated from other fields with blank line:

```python
fields = (
    'id',
    'user',
    'title',

    'acl', # annotation set on model by view
    'something_extra', # dynamic atribute coming from "SerializerMethodField"

    'api',
    'url',
)
```


### Nested results

Nested results should be included in view or viewset, as part of creating dict of serialized data for `Response` object:

```python
data = UserSerializer(user).data
data['post_set'] = UserPostSerializer(posts, many=True).data
return Response(data)
```

The added key should be model's `related_name` in respect of model it annotates (defautly its `modelname_set`).