├── full.sh
├── ideal.sh
├── basic
├── myproject
│ ├── __init__.py
│ ├── core
│ │ ├── __init__.py
│ │ ├── migrations
│ │ │ ├── __init__.py
│ │ │ ├── 0006_auto_20170126_2311.py
│ │ │ ├── 0005_person_slug.py
│ │ │ ├── 0002_auto_20161128_1711.py
│ │ │ ├── 0004_auto_20161128_1810.py
│ │ │ ├── 0001_initial.py
│ │ │ └── 0003_auto_20161128_1801.py
│ │ ├── apps.py
│ │ ├── tests
│ │ │ ├── test_view_home.py
│ │ │ ├── __init__.py
│ │ │ └── test_model_person.py
│ │ ├── templates
│ │ │ ├── core
│ │ │ │ ├── person_form.html
│ │ │ │ ├── person_list.html
│ │ │ │ └── person_detail.html
│ │ │ ├── nav.html
│ │ │ ├── index.html
│ │ │ ├── footer.html
│ │ │ ├── pagination.html
│ │ │ └── base.html
│ │ ├── urls.py
│ │ ├── forms.py
│ │ ├── lists.py
│ │ ├── admin.py
│ │ ├── static
│ │ │ └── css
│ │ │ │ ├── main.css
│ │ │ │ └── social.css
│ │ ├── views.py
│ │ └── models.py
│ ├── urls.py
│ ├── wsgi.py
│ ├── selenium
│ │ ├── gen_names.py
│ │ ├── gen_random_values.py
│ │ ├── selenium_person.py
│ │ └── gen_address.py
│ ├── fixtures
│ │ └── shell_person.py
│ └── settings.py
├── requirements
│ ├── base.txt
│ ├── dev.txt
│ └── prod.txt
├── requirements.txt
├── manage.py
├── Makefile
└── fixtures.json
├── .gitignore
├── README.md
├── Makefile
├── _test_mixer.py
├── basic.sh
├── minimal.sh
└── setupfull.sh
/full.sh:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ideal.sh:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/basic/myproject/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/basic/requirements/base.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/basic/myproject/core/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/basic/myproject/core/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/basic/requirements.txt:
--------------------------------------------------------------------------------
1 | -r requirements/dev.txt
--------------------------------------------------------------------------------
/basic/requirements/dev.txt:
--------------------------------------------------------------------------------
1 | -r base.txt
2 | django-debug-toolbar>=1.4
--------------------------------------------------------------------------------
/basic/requirements/prod.txt:
--------------------------------------------------------------------------------
1 | -r base.txt
2 | gunicorn>=19.6
3 | psycopg2>=2.6
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__/
2 | *.py[cod]
3 | *.sqlite3
4 | *.env
5 | *.DS_Store
6 | .venv/
7 | staticfiles/
8 | .ipynb_checkpoints/
9 |
--------------------------------------------------------------------------------
/basic/myproject/core/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class CoreConfig(AppConfig):
5 | name = 'core'
6 |
--------------------------------------------------------------------------------
/basic/myproject/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import include, url
2 | from django.contrib import admin
3 |
4 | urlpatterns = [
5 | url(r'', include('myproject.core.urls', namespace='core')),
6 | url(r'^admin/', admin.site.urls),
7 | ]
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Boilerplate
2 |
3 | Is a test to create Django project with full shell script.
4 |
5 | `Django==1.10.x`
6 |
7 | * [X] minimal
8 |
9 | ```
10 | source minimal.sh
11 | ```
12 |
13 | * [ ] basic
14 | * [ ] ideal
15 | * [ ] full
16 |
--------------------------------------------------------------------------------
/basic/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | install:
2 | pip install -r requirements.txt
3 |
4 | createuser:
5 | ./manage.py createsuperuser --username='admin' --email=''
6 |
7 | clear:
8 | rm -rf myproject
9 | rm -rf __pycache__
10 | rm -rf contrib
11 | rm -f db.sqlite3
12 | rm -f .env
13 | rm -f manage.py
14 | rm -f requirements.txt
15 |
--------------------------------------------------------------------------------
/basic/Makefile:
--------------------------------------------------------------------------------
1 | migrate:
2 | python manage.py makemigrations
3 | python manage.py migrate
4 |
5 | selenium_person:
6 | python myproject/selenium/selenium_person.py
7 |
8 | shell_person:
9 | python manage.py shell < myproject/fixtures/shell_person.py
10 |
11 | backup:
12 | python manage.py dumpdata --format=json --indent=2 > fixtures.json
13 |
14 | load:
15 | python manage.py loaddata fixtures.json
16 |
17 | run:
18 | python manage.py runserver
19 |
--------------------------------------------------------------------------------
/basic/myproject/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for myproject project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/basic/myproject/core/tests/test_view_home.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 |
4 | class HomeTest(TestCase):
5 |
6 | def setUp(self):
7 | self.resp = self.client.get('/')
8 |
9 | def test_get(self):
10 | ''' get / deve retornar status code 200. '''
11 | self.assertEqual(200, self.resp.status_code)
12 |
13 | def test_template(self):
14 | ''' Home deve usar template index.html '''
15 | self.assertTemplateUsed(self.resp, 'index.html')
16 |
--------------------------------------------------------------------------------
/basic/myproject/core/tests/__init__.py:
--------------------------------------------------------------------------------
1 | PERSON_DATA = {
2 | 'gender': 'M',
3 | 'treatment': 'sr',
4 | 'first_name': 'Regis',
5 | 'last_name': 'Santos',
6 | 'slug': 'regis-santos',
7 | 'birthday': '1985-12-01 02:42:30+00:00',
8 | 'email': 'regis@example.com',
9 | 'cpf': '75873211795',
10 | 'address': u'Rua São Leopoldo, 101',
11 | 'complement': 'Apto 303',
12 | 'district': u'Belezinho',
13 | 'city': u'São Paulo',
14 | 'uf': 'SP',
15 | 'cep': '03055000',
16 | 'blocked': False,
17 | }
18 |
--------------------------------------------------------------------------------
/basic/myproject/core/migrations/0006_auto_20170126_2311.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.5 on 2017-01-26 23:11
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('core', '0005_person_slug'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='person',
17 | name='slug',
18 | field=models.SlugField(verbose_name='slug'),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/basic/myproject/core/migrations/0005_person_slug.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.5 on 2017-01-12 00:39
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('core', '0004_auto_20161128_1810'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='person',
17 | name='slug',
18 | field=models.SlugField(blank=True, verbose_name='slug'),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/basic/myproject/core/templates/core/person_form.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% load bootstrap %}
4 |
5 | {% block title %}Person Form{% endblock title %}
6 |
7 | {% block content %}
8 |
9 |
20 |
21 | {% endblock content %}
22 |
--------------------------------------------------------------------------------
/basic/myproject/core/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import include, url
2 | from myproject.core import views as c
3 |
4 | person_patterns = [
5 | url(r'^$', c.PersonList.as_view(), name='person_list'),
6 | url(r'^add/$', c.PersonCreate.as_view(), name='person_add'),
7 | url(r'^(?P[\w-]+)/$', c.person_detail, name='person_detail'),
8 | url(r'^(?P[\w-]+)/edit/$', c.person_update, name='person_edit'),
9 | url(r'^(?P[\w-]+)/delete/$', c.person_delete, name='person_delete'),
10 | ]
11 |
12 | urlpatterns = [
13 | url(r'^$', c.home, name='home'),
14 | url(r'^person/', include(person_patterns)),
15 | ]
16 |
--------------------------------------------------------------------------------
/basic/myproject/core/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from .models import Person
3 |
4 | gender_list = [('M', 'male'), ('F', 'female')]
5 |
6 |
7 | class PersonForm(forms.ModelForm):
8 | gender = forms.ChoiceField(
9 | choices=gender_list, initial='M', widget=forms.RadioSelect)
10 |
11 | class Meta:
12 | model = Person
13 | fields = ['gender', 'treatment', 'first_name', 'last_name', 'slug',
14 | 'email', 'cpf', 'address', 'complement', 'district', 'city',
15 | 'uf', 'cep', 'birthday', 'blocked']
16 |
17 | def clean_cpf(self):
18 | return self.cleaned_data['cpf'] or ''
19 |
--------------------------------------------------------------------------------
/basic/myproject/core/lists.py:
--------------------------------------------------------------------------------
1 | GENDER_LIST = [('M', 'male'), ('F', 'female')]
2 |
3 | TREATMENT_LIST = (
4 | ('a', 'Arq.'),
5 | ('aa', 'Arqa.'),
6 | ('d', 'Dona'),
7 | ('dr', 'Dr.'),
8 | ('dra', 'Dra.'),
9 | ('e', 'Eng.'),
10 | ('ea', u'Engª.'),
11 | ('p', 'Prof.'),
12 | ('pa', 'Profa.'),
13 | ('sr', 'Sr.'),
14 | ('sra', 'Sra.'),
15 | ('srta', 'Srta.'),
16 | )
17 |
18 | PHONE_TYPE = (
19 | ('pri', 'principal'),
20 | ('com', 'comercial'),
21 | ('res', 'residencial'),
22 | ('cel', 'celular'),
23 | ('cl', 'Claro'),
24 | ('oi', 'Oi'),
25 | ('t', 'Tim'),
26 | ('v', 'Vivo'),
27 | ('n', 'Nextel'),
28 | ('fax', 'fax'),
29 | ('o', 'outros'),
30 | )
31 |
--------------------------------------------------------------------------------
/basic/myproject/core/migrations/0002_auto_20161128_1711.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.3 on 2016-11-28 17:11
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('core', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='person',
17 | name='cpf',
18 | field=models.CharField(blank=True, max_length=11, unique=True, verbose_name='CPF'),
19 | ),
20 | migrations.AlterField(
21 | model_name='person',
22 | name='phone',
23 | field=models.CharField(blank=True, default='', max_length=20),
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/basic/myproject/selenium/gen_names.py:
--------------------------------------------------------------------------------
1 | import random
2 | import names
3 |
4 | """ List of values for use in choices in models. """
5 | treatment_male_list = ('Arq', 'Dr', 'Eng', 'Prof', 'Sr',)
6 |
7 | treatment_female_list = ('Arqa', 'Dona', 'Engª', 'Profa', 'Sra', 'Srta',)
8 |
9 |
10 | def gen_male_first_name():
11 | treatment = random.choice(treatment_male_list)
12 | first_name = names.get_first_name(gender='male')
13 | c = {'treatment': treatment, 'first_name': first_name}
14 | return c
15 |
16 |
17 | def gen_female_first_name():
18 | treatment = random.choice(treatment_female_list)
19 | first_name = names.get_first_name(gender='female')
20 | c = {'treatment': treatment, 'first_name': first_name}
21 | return c
22 |
23 |
24 | def gen_last_name():
25 | return names.get_last_name()
26 |
--------------------------------------------------------------------------------
/basic/myproject/core/admin.py:
--------------------------------------------------------------------------------
1 | from daterange_filter.filter import DateRangeFilter
2 | from django.contrib import admin
3 | from .models import Person, Phone
4 | from .forms import PersonForm
5 |
6 |
7 | class PhoneInline(admin.TabularInline):
8 | model = Phone
9 | extra = 1
10 |
11 |
12 | @admin.register(Person)
13 | class PersonAdmin(admin.ModelAdmin):
14 | inlines = [PhoneInline]
15 | list_display = ('__str__', 'slug', 'gender', 'email', 'cpf',
16 | 'uf', 'birthday', 'created', 'blocked')
17 | date_hierarchy = 'created'
18 | search_fields = ('first_name', 'last_name', 'email', 'cpf')
19 | list_filter = (
20 | 'gender',
21 | ('created', DateRangeFilter),
22 | )
23 | form = PersonForm
24 |
25 | def phone(self, obj):
26 | return obj.phone_set.first()
27 |
28 | phone.short_description = 'phone'
29 |
--------------------------------------------------------------------------------
/basic/myproject/core/templates/nav.html:
--------------------------------------------------------------------------------
1 |
2 |
22 |
--------------------------------------------------------------------------------
/basic/myproject/core/templates/index.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 |
5 |
6 |
Welcome!
7 |
Contacts list make in Django.
8 |
The project contains:
9 |
10 | - Core App
11 | - Person Model
12 | - Person List
13 | - Person Detail
14 | - Person Form
15 | - Person Admin
16 | - Tests with Selenium
17 | - Fixtures with Generate Random Values
18 |
19 |
20 |
23 |
24 |
25 |
26 | {% endblock content %}
27 |
--------------------------------------------------------------------------------
/basic/myproject/core/tests/test_model_person.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from django.shortcuts import resolve_url as r
3 | from django.test import TestCase
4 | from myproject.core.models import Person
5 | from myproject.core.tests import PERSON_DATA
6 |
7 |
8 | class PersonTest(TestCase):
9 |
10 | def setUp(self):
11 | self.obj = Person.objects.create(**PERSON_DATA)
12 |
13 | def test_create(self):
14 | self.assertTrue(Person.objects.exists())
15 |
16 | def test_created(self):
17 | ''' Person must have an auto created attr. '''
18 | self.assertIsInstance(self.obj.created, datetime)
19 |
20 | def test_str(self):
21 | self.assertEqual('Sr. Regis Santos', str(self.obj))
22 |
23 | def test_ordering(self):
24 | self.assertListEqual(['first_name'], Person._meta.ordering)
25 |
26 | def test_get_absolute_url(self):
27 | url = r('core:person_detail', slug=self.obj.slug)
28 | self.assertEqual(url, self.obj.get_absolute_url())
29 |
--------------------------------------------------------------------------------
/basic/myproject/core/templates/footer.html:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/basic/myproject/core/static/css/main.css:
--------------------------------------------------------------------------------
1 | /* Sticky footer styles
2 | -------------------------------------------------- */
3 | /* http://getbootstrap.com/examples/sticky-footer-navbar/sticky-footer-navbar.css */
4 | /* http://getbootstrap.com/2.3.2/examples/sticky-footer.html */
5 | html {
6 | position: relative;
7 | min-height: 100%;
8 | }
9 | body {
10 | /* Margin bottom by footer height */
11 | margin-bottom: 50px;
12 | }
13 | #footer {
14 | position: absolute;
15 | bottom: 0;
16 | width: 100%;
17 | /* Set the fixed height of the footer here */
18 | height: 60px;
19 | background-color: #101010;
20 | }
21 | .credit {
22 | /* Center vertical text */
23 | margin: 20px 0;
24 | }
25 | /* Lastly, apply responsive CSS fixes as necessary */
26 | @media (max-width: 767px) {
27 | body {
28 | margin-bottom: 120px;
29 | }
30 |
31 | #footer {
32 | height: 120px;
33 | padding-left: 5px;
34 | padding-right: 5px;
35 | }
36 | }
37 | /* My personal styles. */
38 | .ok {
39 | color: #44AD41; /*verde*/
40 | }
41 |
42 | .no {
43 | color: #DE2121; /*vermelho*/
44 | }
45 |
--------------------------------------------------------------------------------
/basic/myproject/core/templates/pagination.html:
--------------------------------------------------------------------------------
1 |
2 |
24 |
--------------------------------------------------------------------------------
/basic/myproject/core/migrations/0004_auto_20161128_1810.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.3 on 2016-11-28 18:10
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import django.db.models.deletion
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('core', '0003_auto_20161128_1801'),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='Phone',
18 | fields=[
19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20 | ('phone', models.CharField(blank=True, max_length=20)),
21 | ('phone_type', models.CharField(choices=[('pri', 'principal'), ('com', 'comercial'), ('res', 'residencial'), ('cel', 'celular'), ('cl', 'Claro'), ('oi', 'Oi'), ('t', 'Tim'), ('v', 'Vivo'), ('n', 'Nextel'), ('fax', 'fax'), ('o', 'outros')], default='pri', max_length=3)),
22 | ],
23 | ),
24 | migrations.RemoveField(
25 | model_name='person',
26 | name='phone',
27 | ),
28 | migrations.AddField(
29 | model_name='phone',
30 | name='person',
31 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Person'),
32 | ),
33 | ]
34 |
--------------------------------------------------------------------------------
/_test_mixer.py:
--------------------------------------------------------------------------------
1 | from mixer.backend.django import mixer
2 | from myproject.core.models import Person
3 |
4 | person = mixer.blend(Person)
5 | person.gender
6 | person.treatment
7 | person.first_name
8 | person.last_name
9 | person.cpf
10 | person.birthday
11 | person.email
12 | person.phone
13 | person.blocked
14 |
15 | mixer.blend(Person).birthday
16 |
17 |
18 | #-----------------------------------------
19 | from mixer.backend.django import mixer
20 | from myproject.core.models import Person
21 |
22 | mixer = Mixer(locale='pt_br')
23 | person = mixer.blend(Person)
24 | person.first_name
25 | person.last_name
26 |
27 |
28 | person.faker.name()
29 | # person.faker.gender()
30 | # person.faker.treatment()
31 | person.faker.first_name()
32 | person.faker.last_name()
33 | person.faker.cpf()
34 | person.faker.date_time()
35 | person.faker.iso8601()
36 | person.faker.email()
37 | person.faker.phone_number()
38 | # person.faker.blocked()
39 |
40 | #-----------------------------------------
41 | from mixer.backend.django import Mixer
42 | from myproject.core.models import Person
43 |
44 | person = Mixer(locale='pt_br')
45 | first_name = person.faker.first_name()
46 | last_name = person.faker.last_name()
47 | cpf = person.faker.cpf()
48 | birthday = person.faker.iso8601()
49 | email = person.faker.email()
50 | phone = person.faker.phone_number()
51 |
52 | mixer.blend(Person, first_name=first_name, last_name=last_name,
53 | cpf=cpf, birthday=birthday, email=email, phone=phone)
54 |
--------------------------------------------------------------------------------
/basic/myproject/core/templates/base.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {% block title %}Django{% endblock title %}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
30 |
31 |
32 |
33 |
34 | {% include "nav.html" %}
35 |
36 |
37 |
38 | {% block content %}{% endblock content %}
39 |
40 |
41 |
42 | {% include "footer.html" %}
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/basic/myproject/core/views.py:
--------------------------------------------------------------------------------
1 | from django.core.urlresolvers import reverse_lazy as r
2 | from django.db.models import Q
3 | from django.shortcuts import render
4 | from django.views.generic import CreateView, ListView, DetailView
5 | from django.views.generic import UpdateView, DeleteView
6 | from .models import Person
7 | from .forms import PersonForm
8 |
9 |
10 | def home(request):
11 | return render(request, 'index.html')
12 |
13 |
14 | class CounterMixin(object):
15 |
16 | def get_context_data(self, **kwargs):
17 | context = super(CounterMixin, self).get_context_data(**kwargs)
18 | context['count'] = self.get_queryset().count()
19 | return context
20 |
21 |
22 | class PersonList(CounterMixin, ListView):
23 | template_name = 'core/person_list.html'
24 | model = Person
25 | context_object_name = 'persons'
26 | paginate_by = 10
27 |
28 | def get_queryset(self):
29 | persons = Person.objects.all()
30 | q = self.request.GET.get('search_box')
31 | if q is not None:
32 | persons = persons.filter(
33 | Q(first_name__icontains=q) |
34 | Q(last_name__icontains=q))
35 | return persons
36 |
37 |
38 | class PersonCreate(CreateView):
39 | template_name = 'core/person_form.html'
40 | form_class = PersonForm
41 | success_url = r('core:person_list')
42 |
43 |
44 | person_detail = DetailView.as_view(model=Person)
45 |
46 | person_update = UpdateView.as_view(model=Person, form_class=PersonForm)
47 |
48 | person_delete = DeleteView.as_view(model=Person,
49 | success_url=r('core:person_list'))
50 |
--------------------------------------------------------------------------------
/basic/myproject/core/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.3 on 2016-11-28 16:52
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | initial = True
11 |
12 | dependencies = [
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='Person',
18 | fields=[
19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20 | ('created', models.DateTimeField(auto_now_add=True)),
21 | ('modified', models.DateTimeField(auto_now=True)),
22 | ('gender', models.CharField(choices=[('M', 'male'), ('F', 'female')], max_length=1)),
23 | ('treatment', models.CharField(choices=[('a', 'Arq.'), ('aa', 'Arqa.'), ('d', 'Dona'), ('dr', 'Dr.'), ('dra', 'Dra.'), ('e', 'Eng.'), ('ea', 'Engª.'), ('p', 'Prof.'), ('pa', 'Profa.'), ('sr', 'Sr.'), ('sra', 'Sra.'), ('srta', 'Srta.')], default='', max_length=4)),
24 | ('first_name', models.CharField(max_length=30, verbose_name='first name')),
25 | ('last_name', models.CharField(max_length=30, verbose_name='last name')),
26 | ('cpf', models.CharField(max_length=11, unique=True, verbose_name='CPF')),
27 | ('birthday', models.DateTimeField(blank=True, null=True)),
28 | ('email', models.EmailField(blank=True, max_length=254)),
29 | ('phone', models.CharField(default='', max_length=20)),
30 | ('blocked', models.BooleanField(default=False)),
31 | ],
32 | options={
33 | 'verbose_name': 'contact',
34 | 'verbose_name_plural': 'contacts',
35 | 'ordering': ['first_name'],
36 | },
37 | ),
38 | ]
39 |
--------------------------------------------------------------------------------
/basic/myproject/core/migrations/0003_auto_20161128_1801.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.10.3 on 2016-11-28 18:01
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('core', '0002_auto_20161128_1711'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='person',
17 | name='address',
18 | field=models.CharField(blank=True, max_length=100),
19 | ),
20 | migrations.AddField(
21 | model_name='person',
22 | name='cep',
23 | field=models.CharField(blank=True, max_length=9, verbose_name='CEP'),
24 | ),
25 | migrations.AddField(
26 | model_name='person',
27 | name='city',
28 | field=models.CharField(blank=True, max_length=100),
29 | ),
30 | migrations.AddField(
31 | model_name='person',
32 | name='complement',
33 | field=models.CharField(blank=True, max_length=100),
34 | ),
35 | migrations.AddField(
36 | model_name='person',
37 | name='district',
38 | field=models.CharField(blank=True, max_length=100),
39 | ),
40 | migrations.AddField(
41 | model_name='person',
42 | name='uf',
43 | field=models.CharField(blank=True, choices=[('AC', 'Acre'), ('AL', 'Alagoas'), ('AP', 'Amapá'), ('AM', 'Amazonas'), ('BA', 'Bahia'), ('CE', 'Ceará'), ('DF', 'Distrito Federal'), ('ES', 'Espírito Santo'), ('GO', 'Goiás'), ('MA', 'Maranhão'), ('MT', 'Mato Grosso'), ('MS', 'Mato Grosso do Sul'), ('MG', 'Minas Gerais'), ('PA', 'Pará'), ('PB', 'Paraíba'), ('PR', 'Paraná'), ('PE', 'Pernambuco'), ('PI', 'Piauí'), ('RJ', 'Rio de Janeiro'), ('RN', 'Rio Grande do Norte'), ('RS', 'Rio Grande do Sul'), ('RO', 'Rondônia'), ('RR', 'Roraima'), ('SC', 'Santa Catarina'), ('SP', 'São Paulo'), ('SE', 'Sergipe'), ('TO', 'Tocantins')], max_length=2, verbose_name='UF'),
44 | ),
45 | ]
46 |
--------------------------------------------------------------------------------
/basic/myproject/selenium/gen_random_values.py:
--------------------------------------------------------------------------------
1 | import string
2 | from random import random, randrange, choice
3 | from datetime import date, datetime, timedelta
4 |
5 |
6 | def gen_cpf():
7 | def calcula_digito(digs):
8 | s = 0
9 | qtd = len(digs)
10 | for i in range(qtd):
11 | s += n[i] * (1 + qtd - i)
12 | res = 11 - s % 11
13 | if res >= 10:
14 | return 0
15 | return res
16 | n = [randrange(10) for i in range(9)]
17 | n.append(calcula_digito(n))
18 | n.append(calcula_digito(n))
19 | return "%d%d%d%d%d%d%d%d%d%d%d" % tuple(n)
20 |
21 |
22 | def gen_digits(max_length):
23 | return str(''.join(choice(string.digits) for i in range(max_length)))
24 |
25 |
26 | def gen_phone():
27 | # gera um telefone no formato xx xxxxx-xxxx
28 | digits_ = gen_digits(11)
29 | return '{} 9{}-{}'.format(digits_[:2], digits_[3:7], digits_[7:])
30 |
31 |
32 | def gen_date(min_year=1900, max_year=datetime.now().year):
33 | # gera um date no formato yyyy-mm-dd
34 | start = date(min_year, 1, 1)
35 | years = max_year - min_year + 1
36 | end = start + timedelta(days=365 * years)
37 | return start + (end - start) * random()
38 |
39 |
40 | def convert_date(d):
41 | # converte data no formato mês, dia, ano.
42 | return d.strftime('%m/%d/%Y')
43 |
44 |
45 | def gen_datetime(min_year=1900, max_year=datetime.now().year):
46 | # gera um datetime no formato yyyy-mm-dd hh:mm:ss.000000
47 | start = datetime(min_year, 1, 1)
48 | years = max_year - min_year + 1
49 | end = start + timedelta(days=365 * years)
50 | return (start + (end - start) * random()).isoformat(" ")
51 |
52 |
53 | def gen_timestamp(min_year=1915, max_year=datetime.now().year):
54 | # gera um datetime no formato yyyy-mm-dd hh:mm:ss.000000
55 | min_date = datetime(min_year, 1, 1)
56 | max_date = datetime(max_year + 1, 1, 1)
57 | delta = random() * (max_date - min_date).total_seconds()
58 | return (min_date + timedelta(seconds=delta)).isoformat(" ")
59 |
60 | # See more gen_random_values in
61 | # https://gist.github.com/rg3915/744aacde209046901748
62 |
--------------------------------------------------------------------------------
/basic/myproject/selenium/selenium_person.py:
--------------------------------------------------------------------------------
1 | import time
2 | from random import randint
3 | from decouple import config
4 | from selenium import webdriver
5 | from gen_address import address, district, city, state_name, complement
6 | from gen_names import gen_male_first_name, gen_female_first_name, gen_last_name
7 | from gen_random_values import gen_digits, gen_cpf, gen_date, convert_date
8 |
9 |
10 | HOME = config('HOME')
11 | # page = webdriver.Firefox()
12 | page = webdriver.Chrome(executable_path=HOME + '/chromedriver/chromedriver')
13 | page.maximize_window()
14 | time.sleep(0.5)
15 | page.get('http://localhost:8000/person/add/')
16 |
17 | INDEX = randint(0, 146)
18 |
19 | g = randint(0, 1)
20 |
21 | if g:
22 | gender = 'id_gender_1'
23 | dict_ = gen_female_first_name()
24 | else:
25 | gender = 'id_gender_0'
26 | dict_ = gen_male_first_name()
27 |
28 | treatment = dict_['treatment']
29 | first_name = dict_['first_name']
30 | last_name = gen_last_name()
31 | print(first_name, last_name)
32 |
33 | slug = '{}-{}'.format(first_name.lower(), last_name.lower())
34 |
35 | email = slug + '@example.com'
36 |
37 | cep = '{}-{}'.format(gen_digits(5), gen_digits(3))
38 |
39 | complement_ = '{} {}'.format(complement(), gen_digits(2))
40 |
41 | photo = 'http://icons.iconarchive.com/icons/icons-land/vista-people/256/Office-Customer-Male-Light-icon.png'
42 |
43 | search = page.find_element_by_id(gender)
44 | search.click()
45 |
46 | fields = [
47 | ['id_treatment', treatment],
48 | ['id_first_name', first_name],
49 | ['id_last_name', last_name],
50 | ['id_slug', slug],
51 | ['id_email', email],
52 | ['id_cpf', gen_cpf()],
53 | ['id_address', address()],
54 | ['id_complement', complement_],
55 | ['id_district', district()],
56 | ['id_city', city()],
57 | ['id_uf', state_name()],
58 | ['id_cep', cep],
59 | ['id_birthday', convert_date(gen_date())],
60 | ]
61 |
62 | for field in fields:
63 | search = page.find_element_by_id(field[0])
64 | search.send_keys(field[1])
65 | time.sleep(0.2)
66 |
67 |
68 | # button = page.find_element_by_id('id_submit')
69 | button = page.find_element_by_class_name('btn-primary')
70 | # button.click()
71 |
72 | # page.quit()
73 |
--------------------------------------------------------------------------------
/basic/myproject/core/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.core.urlresolvers import reverse_lazy
3 | from localflavor.br.br_states import STATE_CHOICES
4 | # List of values for use in choices
5 | from .lists import GENDER_LIST, TREATMENT_LIST, PHONE_TYPE
6 |
7 |
8 | class TimeStampedModel(models.Model):
9 | created = models.DateTimeField(auto_now_add=True, auto_now=False)
10 | modified = models.DateTimeField(auto_now_add=False, auto_now=True)
11 |
12 | class Meta:
13 | abstract = True
14 |
15 |
16 | class Address(models.Model):
17 | address = models.CharField(max_length=100, blank=True)
18 | complement = models.CharField(max_length=100, blank=True)
19 | district = models.CharField(max_length=100, blank=True)
20 | city = models.CharField(max_length=100, blank=True)
21 | uf = models.CharField('UF', max_length=2,
22 | choices=STATE_CHOICES, blank=True)
23 | cep = models.CharField('CEP', max_length=9, blank=True)
24 |
25 | class Meta:
26 | abstract = True
27 |
28 |
29 | class Person(TimeStampedModel, Address):
30 | gender = models.CharField(max_length=1, choices=GENDER_LIST)
31 | treatment = models.CharField(
32 | max_length=4, choices=TREATMENT_LIST, default='')
33 | slug = models.SlugField('slug')
34 | first_name = models.CharField('first name', max_length=30)
35 | last_name = models.CharField('last name', max_length=30)
36 | cpf = models.CharField('CPF', max_length=11, unique=True, blank=True)
37 | birthday = models.DateTimeField(null=True, blank=True)
38 | email = models.EmailField(blank=True)
39 | blocked = models.BooleanField(default=False)
40 |
41 | class Meta:
42 | ordering = ['first_name']
43 | verbose_name = 'contact'
44 | verbose_name_plural = 'contacts'
45 |
46 | def __str__(self):
47 | fname = [self.get_treatment_display(), self.first_name, self.last_name]
48 | return ' '.join(filter(None, fname))
49 |
50 | full_name = property(__str__)
51 |
52 | def get_absolute_url(self):
53 | ''' return slug '''
54 | return reverse_lazy('core:person_detail', kwargs={'slug': self.slug})
55 |
56 |
57 | class Phone(models.Model):
58 | phone = models.CharField(max_length=20, blank=True)
59 | person = models.ForeignKey('Person')
60 | phone_type = models.CharField(
61 | max_length=3, choices=PHONE_TYPE, default='pri')
62 |
63 | def __str__(self):
64 | return self.phone
65 |
--------------------------------------------------------------------------------
/basic/myproject/fixtures/shell_person.py:
--------------------------------------------------------------------------------
1 | import random
2 | import names
3 | from myproject.core.models import Person, Phone
4 | from myproject.selenium.gen_address import address, district, city, state_uf, complement
5 | from myproject.selenium.gen_names import gen_male_first_name, gen_female_first_name, gen_last_name
6 | from myproject.selenium.gen_random_values import gen_digits, gen_cpf, gen_timestamp, gen_phone
7 |
8 |
9 | PHONE_TYPE = ('pri', 'com', 'res', 'cel')
10 | person_list = []
11 | phone_list = []
12 | REPEAT = 20
13 |
14 |
15 | def person_data():
16 | gender = random.choice(['M', 'F'])
17 | if gender == 'M':
18 | treatment = gen_male_first_name()['treatment']
19 | first_name = gen_male_first_name()['first_name']
20 | else:
21 | treatment = gen_female_first_name()['treatment']
22 | first_name = gen_female_first_name()['first_name']
23 | last_name = names.get_last_name()
24 | slug = '{}-{}'.format(first_name.lower(), last_name.lower())
25 | email = slug + '@example.com'
26 | birthday = gen_timestamp() + '+00'
27 | blocked = random.choice([1, 0])
28 | cep = '{}-{}'.format(gen_digits(5), gen_digits(3))
29 | complement_ = '{} {}'.format(complement(), gen_digits(2))
30 | return {'gender': gender,
31 | 'treatment': treatment,
32 | 'first_name': first_name,
33 | 'last_name': last_name,
34 | 'slug': slug,
35 | 'cpf': gen_cpf(),
36 | 'birthday': birthday,
37 | 'email': email,
38 | 'address': address(),
39 | 'complement': complement_,
40 | 'district': district(),
41 | 'city': city(),
42 | 'uf': state_uf(),
43 | 'cep': cep,
44 | 'blocked': blocked,
45 | }
46 |
47 |
48 | # Appending person_list
49 | for _ in range(REPEAT):
50 | person_list.append(person_data())
51 |
52 | # Insert Persons
53 | obj = [Person(**person) for person in person_list]
54 | Person.objects.bulk_create(obj)
55 |
56 | # Appending phone_list
57 | persons = Person.objects.all()
58 | for person in persons:
59 | for _ in range(1, random.randint(2, 5)):
60 | phone_list.append({
61 | 'person': person,
62 | 'phone': gen_phone(),
63 | 'phone_type': random.choice(PHONE_TYPE),
64 | })
65 |
66 | # Insert Phones
67 | obj = [Phone(**phone) for phone in phone_list]
68 | Phone.objects.bulk_create(obj)
69 |
70 | print('\n%d Persons salvo com sucesso.' % REPEAT)
71 |
--------------------------------------------------------------------------------
/basic/myproject/core/templates/core/person_list.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block title %}Contacts{% endblock title %}
4 |
5 | {% block content %}
6 |
7 |
20 |
21 | Contacts List
22 |
23 | {% if persons %}
24 |
25 |
26 |
27 | | Name |
28 | Email |
29 | Phone |
30 | CPF |
31 | Birthday |
32 | Blocked |
33 |
34 |
35 |
36 | {% for person in persons %}
37 | {% if person.blocked %}
38 |
39 | {% else %}
40 |
41 | {% endif %}
42 | | {{ person.full_name }} |
43 | {{ person.email }} |
44 | {% if person.phone_set.first %}
45 | {{ person.phone_set.first }}
46 | {% if person.phone_set.count > 1 %}
47 | +{{ person.phone_set.count|add:"-1" }}
48 | {% endif %}
49 | |
50 | {% else %}
51 | --- |
52 | {% endif %}
53 | {{ person.cpf }} |
54 | {{ person.birthday|date:"d/m/Y" }} |
55 | {% if person.blocked %}
56 | |
57 | {% else %}
58 | |
59 | {% endif %}
60 |
61 | {% endfor %}
62 |
63 |
64 | {% else %}
65 |
66 |
Without items in this list.
67 |
68 |
69 |
72 |
73 |
74 | {% endif %}
75 |
76 |
77 | {% if count > 0 %}
78 |
79 | {{ persons|length }} contact{{ persons|length|pluralize }}
80 | Total: {{ page_obj.paginator.count }} contact{{ page_obj.paginator.count|pluralize }}
81 | {% endif %}
82 |
83 |
84 |
85 | {% if count > 0 %}
86 | {% include "pagination.html" %}
87 | {% endif %}
88 |
89 | {% endblock content %}
90 |
--------------------------------------------------------------------------------
/basic/myproject/core/static/css/social.css:
--------------------------------------------------------------------------------
1 | /* http://www.kodingmadesimple.com/2014/11/create-stylish-bootstrap-3-social-media-icons.html */
2 | .social {
3 | margin: 0;
4 | padding: 0;
5 | }
6 |
7 | .social ul {
8 | margin: 0;
9 | padding: 5px;
10 | }
11 |
12 | .social ul li {
13 | margin: 5px;
14 | list-style: none outside none;
15 | display: inline-block;
16 | }
17 |
18 | .social i {
19 | width: 40px;
20 | height: 40px;
21 | color: #FFF;
22 | background-color: #909AA0;
23 | font-size: 22px;
24 | text-align:center;
25 | padding-top: 12px;
26 | border-radius: 50%;
27 | -moz-border-radius: 50%;
28 | -webkit-border-radius: 50%;
29 | -o-border-radius: 50%;
30 | transition: all ease 0.3s;
31 | -moz-transition: all ease 0.3s;
32 | -webkit-transition: all ease 0.3s;
33 | -o-transition: all ease 0.3s;
34 | -ms-transition: all ease 0.3s;
35 | text-decoration: none;
36 | }
37 |
38 | .social .fa-facebook {
39 | background: #4060A5;
40 | }
41 |
42 | .social .fa-twitter {
43 | background: #00ABE3;
44 | }
45 |
46 | .social .fa-google-plus {
47 | background: #e64522;
48 | }
49 |
50 | .social .fa-github {
51 | background: #343434;
52 | }
53 |
54 | .social .fa-pinterest {
55 | background: #cb2027;
56 | }
57 |
58 | .social .fa-linkedin {
59 | background: #0094BC;
60 | }
61 |
62 | .social .fa-flickr {
63 | background: #FF57AE;
64 | }
65 |
66 | .social .fa-instagram {
67 | background: #375989;
68 | }
69 |
70 | .social .fa-vimeo-square {
71 | background: #83DAEB;
72 | }
73 |
74 | .social .fa-stack-overflow {
75 | background: #FEA501;
76 | }
77 |
78 | .social .fa-dropbox {
79 | background: #017FE5;
80 | }
81 |
82 | .social .fa-tumblr {
83 | background: #3a5876;
84 | }
85 |
86 | .social .fa-dribbble {
87 | background: #F46899;
88 | }
89 |
90 | .social .fa-skype {
91 | background: #00C6FF;
92 | }
93 |
94 | .social .fa-stack-exchange {
95 | background: #4D86C9;
96 | }
97 |
98 | .social .fa-youtube {
99 | background: #FF1F25;
100 | }
101 |
102 | .social .fa-xing {
103 | background: #005C5E;
104 | }
105 |
106 | .social .fa-rss {
107 | background: #e88845;
108 | }
109 |
110 | .social .fa-foursquare {
111 | background: #09B9E0;
112 | }
113 |
114 | .social .fa-youtube-play {
115 | background: #DF192A;
116 | }
117 |
118 | .social .fa-telegram {
119 | background: #0088cc;
120 | }
121 |
122 | .social .fa-slack {
123 | background: #4F3A4B;
124 | }
125 |
126 | .social .fa-whatsapp {
127 | background: #65BC54;
128 | }
129 |
130 | .socialfooter {
131 | margin: 0;
132 | padding: 0;
133 | }
134 |
135 | .socialfooter ul {
136 | margin: 0;
137 | padding: 5px;
138 | }
139 |
140 | .socialfooter ul li {
141 | margin: 5px;
142 | list-style: none outside none;
143 | display: inline-block;
144 | }
145 |
146 | .socialfooter i {
147 | color: #FFF;
148 | font-size: 22px;
149 | text-align:center;
150 | padding-top: 12px;
151 | border-radius: 50%;
152 | -moz-border-radius: 50%;
153 | -webkit-border-radius: 50%;
154 | -o-border-radius: 50%;
155 | transition: all ease 0.3s;
156 | -moz-transition: all ease 0.3s;
157 | -webkit-transition: all ease 0.3s;
158 | -o-transition: all ease 0.3s;
159 | -ms-transition: all ease 0.3s;
160 | text-decoration: none;
161 | }
162 |
163 | .socialfooter i:hover {
164 | color: #00ABE3;
165 | }
166 |
--------------------------------------------------------------------------------
/basic/myproject/selenium/gen_address.py:
--------------------------------------------------------------------------------
1 | import names
2 | from random import choice, randint
3 |
4 |
5 | street_prefixes = ('Alameda', 'Avenida', 'Estrada',
6 | 'Largo', 'Praça', 'Rua', 'Travessa')
7 |
8 |
9 | districts = ('Acaiaca', 'Aguas Claras', 'Alpes', 'Alto Barroca',
10 | 'Alto Dos Pinheiros', 'Alto Vera Cruz', 'Anchieta', 'Bacurau',
11 | 'Bandeirantes', 'Barreiro', 'Barroca', 'Belmonte', 'Bonfim',
12 | 'Bonsucesso', 'Cachoeirinha', 'Caiçaras', 'Califórnia',
13 | 'Diamante', 'Dom Joaquim', 'Embaúbas', 'Esplanada', 'Estrela',
14 | 'Floramar', 'Floresta', 'Garças', 'Glória', 'Grajaú',
15 | 'Horto Florestal', 'Indaiá', 'Ipe', 'Ipiranga', 'Itaipu',
16 | 'Jaraguá', 'Jardim Alvorada', 'Jardinópolis', 'Jatobá',
17 | 'Lagoa', 'Laranjeiras', 'Liberdade', 'Madri', 'Manacas',
18 | 'Miramar', 'Nova America', 'Novo Tupi', 'Oeste', 'Pantanal',
19 | 'Renascença', 'Santa Inês', 'Santa Sofia', 'Teixeira Dias',
20 | 'Tiradentes', 'Urca', 'Vera Cruz', 'Vila Da Luz', 'Vila Pilar',
21 | 'Vitoria', 'Xangri-Lá', 'Zilah Sposito', )
22 |
23 | states = (
24 | ('AC', 'Acre'),
25 | ('AL', 'Alagoas'),
26 | ('AP', 'Amapá'),
27 | ('AM', 'Amazonas'),
28 | ('BA', 'Bahia'),
29 | ('CE', 'Ceará'),
30 | ('DF', 'Distrito Federal'),
31 | ('ES', 'Espírito Santo'),
32 | ('GO', 'Goiás'),
33 | ('MA', 'Maranhão'),
34 | ('MT', 'Mato Grosso'),
35 | ('MS', 'Mato Grosso do Sul'),
36 | ('MG', 'Minas Gerais'),
37 | ('PA', 'Pará'),
38 | ('PB', 'Paraíba'),
39 | ('PR', 'Paraná'),
40 | ('PE', 'Pernambuco'),
41 | ('PI', 'Piauí'),
42 | ('RJ', 'Rio de Janeiro'),
43 | ('RN', 'Rio Grande do Norte'),
44 | ('RS', 'Rio Grande do Sul'),
45 | ('RO', 'Rondônia'),
46 | ('RR', 'Roraima'),
47 | ('SC', 'Santa Catarina'),
48 | ('SP', 'São Paulo'),
49 | ('SE', 'Sergipe'),
50 | ('TO', 'Tocantins'))
51 |
52 |
53 | complements = ('andar', 'apto', 'casa', 'fundos', 'loja')
54 |
55 |
56 | def random_element(elements):
57 | return choice(elements)
58 |
59 |
60 | def gen_first_name():
61 | return names.get_first_name()
62 |
63 |
64 | def gen_last_name():
65 | return names.get_last_name()
66 |
67 |
68 | def address():
69 | """
70 | :example 'Rua Eurides da Cunha, 116'
71 | """
72 | street_prefix = random_element(street_prefixes)
73 | first_name = gen_first_name()
74 | last_name = gen_last_name()
75 | gen_number = randint(1, 9999)
76 | pattern = '{} {} {}, {}'.format(
77 | street_prefix, first_name, last_name, gen_number)
78 | return pattern
79 |
80 |
81 | def city():
82 | return gen_last_name()
83 |
84 |
85 | def state():
86 | """
87 | Randomly returns a Brazilian State ('sigla' , 'nome').
88 | :example ('MG' . 'Minas Gerais')
89 | """
90 | return random_element(states)
91 |
92 |
93 | def state_name():
94 | """
95 | Randomly returns a Brazilian State Name
96 | :example 'Minas Gerais'
97 | """
98 | return state()[1]
99 |
100 |
101 | def state_uf():
102 | """
103 | Randomly returns the abbreviation of a Brazilian State
104 | :example 'MG'
105 | """
106 | return state()[0]
107 |
108 |
109 | def district():
110 | """
111 | Randomly returns a bairro (neighborhood) name. The names were taken from the city of Belo Horizonte - Minas Gerais
112 | :example 'Serra'
113 | """
114 | return random_element(districts)
115 |
116 | # https://github.com/joke2k/faker/blob/master/faker/providers/__init__.py
117 |
118 |
119 | def complement():
120 | return random_element(complements)
121 |
--------------------------------------------------------------------------------
/basic/myproject/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for myproject project.
3 |
4 | Generated by 'django-admin startproject' using Django 1.9.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.9/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/1.9/ref/settings/
11 | """
12 |
13 | import os
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = 'tsu--1ww)gv#_%24sqjxdvdu^u52-#ii($0+s!v76647gq6^v5'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = []
29 |
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = [
34 | 'django.contrib.admin',
35 | 'django.contrib.auth',
36 | 'django.contrib.contenttypes',
37 | 'django.contrib.sessions',
38 | 'django.contrib.messages',
39 | 'django.contrib.staticfiles',
40 | # third-party apps
41 | 'bootstrapform',
42 | 'daterange_filter',
43 | # my app
44 | 'myproject.core',
45 | ]
46 |
47 | MIDDLEWARE_CLASSES = [
48 | 'django.middleware.security.SecurityMiddleware',
49 | 'django.contrib.sessions.middleware.SessionMiddleware',
50 | 'django.middleware.common.CommonMiddleware',
51 | 'django.middleware.csrf.CsrfViewMiddleware',
52 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
53 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
54 | 'django.contrib.messages.middleware.MessageMiddleware',
55 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
56 | ]
57 |
58 | ROOT_URLCONF = 'myproject.urls'
59 |
60 | TEMPLATES = [
61 | {
62 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
63 | 'DIRS': [],
64 | 'APP_DIRS': True,
65 | 'OPTIONS': {
66 | 'context_processors': [
67 | 'django.template.context_processors.debug',
68 | 'django.template.context_processors.request',
69 | 'django.contrib.auth.context_processors.auth',
70 | 'django.contrib.messages.context_processors.messages',
71 | ],
72 | },
73 | },
74 | ]
75 |
76 | WSGI_APPLICATION = 'myproject.wsgi.application'
77 |
78 |
79 | # Database
80 | # https://docs.djangoproject.com/en/1.9/ref/settings/#databases
81 |
82 | DATABASES = {
83 | 'default': {
84 | 'ENGINE': 'django.db.backends.sqlite3',
85 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
86 | }
87 | }
88 |
89 |
90 | # Password validation
91 | # https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
92 |
93 | AUTH_PASSWORD_VALIDATORS = [
94 | {
95 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
96 | },
97 | {
98 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
99 | },
100 | {
101 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
102 | },
103 | {
104 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
105 | },
106 | ]
107 |
108 |
109 | # Internationalization
110 | # https://docs.djangoproject.com/en/1.9/topics/i18n/
111 |
112 | LANGUAGE_CODE = 'en-us'
113 |
114 | TIME_ZONE = 'UTC'
115 |
116 | USE_I18N = True
117 |
118 | USE_L10N = True
119 |
120 | USE_TZ = True
121 |
122 |
123 | # Static files (CSS, JavaScript, Images)
124 | # https://docs.djangoproject.com/en/1.9/howto/static-files/
125 |
126 | STATIC_URL = '/static/'
127 |
--------------------------------------------------------------------------------
/basic/myproject/core/templates/core/person_detail.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% load static %}
4 |
5 | {% block title %}Person Detail{% endblock title %}
6 |
7 | {% block content %}
8 |
9 |
14 |
15 |
16 |
17 |

18 |
19 |
20 |
{{ object.full_name }}
21 | {% if object.email %}
22 |
23 | {% endif %}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
94 |
95 |
96 |
99 |
100 |
101 |
104 |
105 |
106 |
107 |
127 |
128 | {% endblock content %}
--------------------------------------------------------------------------------
/basic.sh:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Create requirements
4 | mkdir requirements
5 | touch requirements.txt
6 | touch requirements/{base,dev,prod}.txt
7 |
8 | # Bootstrap templates
9 | mkdir -p myproject/core/templates/core
10 | touch myproject/core/templates/{base,index,nav,pagination}.html
11 | touch myproject/core/templates/core/person_{detail,form,list}.html
12 |
13 | echo "${green}>>> Creating static/css directory${reset}"
14 | mkdir -p myproject/core/static/css
15 |
16 | echo "${green}>>> Creating main.css${reset}"
17 | cat << EOF > myproject/core/static/css/main.css
18 | /* Sticky footer styles
19 | -------------------------------------------------- */
20 | /* http://getbootstrap.com/examples/sticky-footer-navbar/sticky-footer-navbar.css */
21 | /* http://getbootstrap.com/2.3.2/examples/sticky-footer.html */
22 | html {
23 | position: relative;
24 | min-height: 100%;
25 | }
26 | body {
27 | /* Margin bottom by footer height */
28 | margin-bottom: 60px;
29 | }
30 | #footer {
31 | position: absolute;
32 | bottom: 0;
33 | width: 100%;
34 | /* Set the fixed height of the footer here */
35 | height: 60px;
36 | background-color: #101010;
37 | }
38 | .credit {
39 | /* Center vertical text */
40 | margin: 20px 0;
41 | }
42 | /* Lastly, apply responsive CSS fixes as necessary */
43 | @media (max-width: 767px) {
44 | body {
45 | margin-bottom: 120px;
46 | }
47 |
48 | #footer {
49 | height: 120px;
50 | padding-left: 5px;
51 | padding-right: 5px;
52 | }
53 | }
54 | /* My personal styles. */
55 | .ok {
56 | color: #44AD41; /*verde*/
57 | }
58 |
59 | .no {
60 | color: #DE2121; /*vermelho*/
61 | }
62 | EOF
63 |
64 | echo "${green}>>> Creating social.css${reset}"
65 | cat << EOF > myproject/core/static/css/social.css
66 | /* http://www.kodingmadesimple.com/2014/11/create-stylish-bootstrap-3-social-media-icons.html */
67 | .social {
68 | margin: 0;
69 | padding: 0;
70 | }
71 |
72 | .social ul {
73 | margin: 0;
74 | padding: 5px;
75 | }
76 |
77 | .social ul li {
78 | margin: 5px;
79 | list-style: none outside none;
80 | display: inline-block;
81 | }
82 |
83 | .social i {
84 | width: 40px;
85 | height: 40px;
86 | color: #FFF;
87 | background-color: #909AA0;
88 | font-size: 22px;
89 | text-align:center;
90 | padding-top: 12px;
91 | border-radius: 50%;
92 | -moz-border-radius: 50%;
93 | -webkit-border-radius: 50%;
94 | -o-border-radius: 50%;
95 | transition: all ease 0.3s;
96 | -moz-transition: all ease 0.3s;
97 | -webkit-transition: all ease 0.3s;
98 | -o-transition: all ease 0.3s;
99 | -ms-transition: all ease 0.3s;
100 | text-decoration: none;
101 | }
102 |
103 | .social .fa-facebook {
104 | background: #4060A5;
105 | }
106 |
107 | .social .fa-twitter {
108 | background: #00ABE3;
109 | }
110 |
111 | .social .fa-google-plus {
112 | background: #e64522;
113 | }
114 |
115 | .social .fa-github {
116 | background: #343434;
117 | }
118 |
119 | .social .fa-pinterest {
120 | background: #cb2027;
121 | }
122 |
123 | .social .fa-linkedin {
124 | background: #0094BC;
125 | }
126 |
127 | .social .fa-flickr {
128 | background: #FF57AE;
129 | }
130 |
131 | .social .fa-instagram {
132 | background: #375989;
133 | }
134 |
135 | .social .fa-vimeo-square {
136 | background: #83DAEB;
137 | }
138 |
139 | .social .fa-stack-overflow {
140 | background: #FEA501;
141 | }
142 |
143 | .social .fa-dropbox {
144 | background: #017FE5;
145 | }
146 |
147 | .social .fa-tumblr {
148 | background: #3a5876;
149 | }
150 |
151 | .social .fa-dribbble {
152 | background: #F46899;
153 | }
154 |
155 | .social .fa-skype {
156 | background: #00C6FF;
157 | }
158 |
159 | .social .fa-stack-exchange {
160 | background: #4D86C9;
161 | }
162 |
163 | .social .fa-youtube {
164 | background: #FF1F25;
165 | }
166 |
167 | .social .fa-xing {
168 | background: #005C5E;
169 | }
170 |
171 | .social .fa-rss {
172 | background: #e88845;
173 | }
174 |
175 | .social .fa-foursquare {
176 | background: #09B9E0;
177 | }
178 |
179 | .social .fa-youtube-play {
180 | background: #DF192A;
181 | }
182 |
183 | .social .fa-slack {
184 | background: #4F3A4B;
185 | }
186 |
187 | .social .fa-whatsapp {
188 | background: #65BC54;
189 | }
190 |
191 | .socialfooter {
192 | margin: 0;
193 | padding: 0;
194 | }
195 |
196 | .socialfooter ul {
197 | margin: 0;
198 | padding: 5px;
199 | }
200 |
201 | .socialfooter ul li {
202 | margin: 5px;
203 | list-style: none outside none;
204 | display: inline-block;
205 | }
206 |
207 | .socialfooter i {
208 | color: #FFF;
209 | font-size: 22px;
210 | text-align:center;
211 | padding-top: 12px;
212 | border-radius: 50%;
213 | -moz-border-radius: 50%;
214 | -webkit-border-radius: 50%;
215 | -o-border-radius: 50%;
216 | transition: all ease 0.3s;
217 | -moz-transition: all ease 0.3s;
218 | -webkit-transition: all ease 0.3s;
219 | -o-transition: all ease 0.3s;
220 | -ms-transition: all ease 0.3s;
221 | text-decoration: none;
222 | }
223 |
224 | .socialfooter i:hover {
225 | color: #00ABE3;
226 | }
227 | EOF
228 |
229 |
230 | pip install django dj-database-url django-bootstrap-form django-daterange-filter django-localflavor django-widget-tweaks python-decouple pytz selenium django-extensions
231 |
232 | python manage.py makemigrations core
233 | python manage.py migrate
234 |
235 | # Tem que copiar os arquivos
236 | cp fixtures/gen_address.py selenium/gen_address_.py
237 | cp fixtures/gen_names.py selenium/gen_names_.py
238 | cp fixtures/gen_random_values.py selenium/gen_random_values_.py
239 |
--------------------------------------------------------------------------------
/minimal.sh:
--------------------------------------------------------------------------------
1 | # minimal
2 | # Shell script to create a minimal Django project.
3 | # This project require Python3.
4 |
5 | # Download:
6 | # wget --output-document=minimal.sh
7 |
8 | # Usage:
9 | # Type the following command, you can change the project name.
10 |
11 | # source minimal.sh
12 |
13 | # Colors
14 | red=`tput setaf 1`
15 | green=`tput setaf 2`
16 | reset=`tput sgr0`
17 |
18 | # default name project
19 | echo "Type the name of project (default: myproject): "
20 | echo "Digite o nome do projeto (default: myproject): "
21 | read p
22 | PROJECT=${p:-myproject}
23 |
24 | echo "${green}>>> Creating virtualenv${reset}"
25 | python -m venv .venv
26 | echo "${green}>>> .venv is created${reset}"
27 |
28 | # active
29 | sleep 2
30 | echo "${green}>>> activate the .venv${reset}"
31 | source .venv/bin/activate
32 | # shortcut prompt, optional
33 | PS1="(`basename \"$VIRTUAL_ENV\"`)\e[1;34m:/\W\e[00m$ "
34 | sleep 2
35 |
36 | # installdjango
37 | echo "${green}>>> Installing the Django${reset}"
38 | pip install -U pip
39 | pip install django dj-database-url python-decouple django-extensions
40 | pip freeze > requirements.txt
41 |
42 | # Create contrib/secret_gen.py
43 | echo "${green}>>> Creating the contrib/secret_gen.py${reset}"
44 | mkdir -p contrib
45 | cat << EOF > contrib/secret_gen.py
46 | #!/usr/bin/env python
47 |
48 | """
49 | Django SECRET_KEY generator.
50 | """
51 | from django.utils.crypto import get_random_string
52 |
53 |
54 | chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
55 |
56 | CONFIG_STRING = """
57 | DEBUG=True
58 | SECRET_KEY=%s
59 | ALLOWED_HOSTS=127.0.0.1, .localhost
60 | #DATABASE_URL=postgres://USER:PASSWORD@HOST:PORT/NAME
61 | #DEFAULT_FROM_EMAIL=
62 | #EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
63 | #EMAIL_HOST=
64 | #EMAIL_PORT=
65 | #EMAIL_USE_TLS=
66 | #EMAIL_HOST_USER=
67 | #EMAIL_HOST_PASSWORD=
68 | """.strip() % get_random_string(50, chars)
69 |
70 | # Writing our configuration file to '.env'
71 | with open('.env', 'w') as configfile:
72 | configfile.write(CONFIG_STRING)
73 | EOF
74 |
75 | echo "${green}>>> Generate .env${reset}"
76 | python contrib/secret_gen.py
77 |
78 | echo "${green}>>> Creating .gitignore${reset}"
79 | cat << EOF > .gitignore
80 | __pycache__/
81 | *.py[cod]
82 | *.sqlite3
83 | *.env
84 | *.DS_Store
85 | .venv/
86 | staticfiles/
87 | .ipynb_checkpoints/
88 | EOF
89 |
90 | # Create Project
91 | echo "${green}>>> Creating the project '$PROJECT' ...${reset}"
92 | django-admin.py startproject $PROJECT .
93 | cd $PROJECT
94 | # Create App
95 | echo "${green}>>> Creating the app 'core' ...${reset}"
96 | python ../manage.py startapp core
97 |
98 | # up one level
99 | cd ..
100 |
101 | # ********** Editing .env and settings.py **********
102 | # echo "${green}>>> Refactor .env${reset}"
103 | # find SECRET_KEY
104 | # grep "SECRET_KEY" $PROJECT/settings.py > .env
105 | # replace =
106 | # sed -i "s/ = /=/g" .env
107 | # replace '
108 | # sed -i "s/'//g" .env
109 | # cat << EOF >> .env
110 | # DEBUG=True
111 | # ALLOWED_HOSTS=127.0.0.1, .localhost, .herokuapp.com
112 | # EOF
113 |
114 | echo "${green}>>> Editing settings.py${reset}"
115 | # insert text in line below of string
116 | sed -i "/import os/a\from decouple import config, Csv\nfrom dj_database_url import parse as dburl" $PROJECT/settings.py
117 | # remove everything except the 1st n characters in every line - See more at: http://www.theunixschool.com/2014/08/sed-examples-remove-delete-chars-from-line-file.html#sthash.h7FUerys.dpuf
118 | sed -i "/SECRET_KEY/d" $PROJECT/settings.py
119 | # insert text in line below of string
120 | sed -i "/keep the secret/a\SECRET_KEY = config('SECRET_KEY')" $PROJECT/settings.py
121 | # replace text
122 | sed -i "s/DEBUG = True/DEBUG = config('DEBUG', default=False, cast=bool)/g" $PROJECT/settings.py
123 | sed -i "s/ALLOWED_HOSTS\ =\ \[\]/ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=[], cast=Csv())/g" $PROJECT/settings.py
124 | # insert text in line below of string
125 | sed -i "/django.contrib.staticfiles/a\ # thirty apps\n 'django_extensions',\n \# my apps\n '$PROJECT.core'," $PROJECT/settings.py
126 | # exclude lines
127 | sed -i "/DATABASES/d" $PROJECT/settings.py
128 | sed -i "/'default':/d" $PROJECT/settings.py
129 | sed -i "/ENGINE/d" $PROJECT/settings.py
130 | # exclude 3 lines
131 | sed -i "/db.sqlite3/,+3d" $PROJECT/settings.py
132 | # insert text after 'databases'
133 | sed -i "/databases/a default_dburl = 'sqlite:///' + os.path.join(BASE_DIR, 'db.sqlite3')\nDATABASES = {\n 'default': config('DATABASE_URL', default=default_dburl, cast=dburl),\n}" $PROJECT/settings.py
134 | # replace text
135 | sed -i "s/en-us/pt-br/g" $PROJECT/settings.py
136 | # replace text
137 | sed -i "s/UTC/America\/Sao_Paulo/g" $PROJECT/settings.py
138 | # insert text in line below of string
139 | sed -i "/USE_TZ/a\\\nUSE_THOUSAND_SEPARATOR = True\n\nDECIMAL_SEPARATOR = ','" $PROJECT/settings.py
140 | sed -i "/STATIC_URL/a\STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')\n\nLOGIN_URL = '/admin/login/'" $PROJECT/settings.py
141 |
142 | # Create HttpResponse
143 | cat << EOF > $PROJECT/core/views.py
144 | from django.shortcuts import render
145 | from django.http import HttpResponse
146 |
147 |
148 | def home(request):
149 | return HttpResponse('Hello World')
150 | EOF
151 |
152 | # Create core/urls.py
153 | cat << EOF > $PROJECT/core/urls.py
154 | from django.conf.urls import url
155 | from $PROJECT.core import views as c
156 |
157 | urlpatterns = [
158 | url(r'^$', c.home, name='home'),
159 | ]
160 | EOF
161 |
162 | # Editing urls.py
163 | cat << EOF > $PROJECT/urls.py
164 | from django.conf.urls import include, url
165 | from django.contrib import admin
166 |
167 | urlpatterns = [
168 | url(r'', include('$PROJECT.core.urls', namespace='core')),
169 | url(r'^admin/', admin.site.urls),
170 | ]
171 | EOF
172 |
173 | # Running migrate
174 | echo "${green}>>> Running migrate ...${reset}"
175 | python manage.py migrate
176 |
177 | # Running tests
178 | echo "${green}>>> Running tests ...${reset}"
179 | python manage.py test
180 |
181 | echo "${green}>>> Creating Makefile${reset}"
182 | cat << EOF > Makefile
183 | install:
184 | tabpip install -r requirements.txt
185 |
186 | createuser:
187 | tab./manage.py createsuperuser --username='admin' --email=''
188 |
189 | clear:
190 | tabrm -rf myproject
191 | tabrm -rf __pycache__
192 | tabrm -f db.sqlite3
193 | tabrm -f .env
194 | tabrm -f manage.py
195 | tabrm -f requirements.txt
196 | EOF
197 |
198 | # Replace tab to \t
199 | sed -i "s/tab/\t/g" Makefile
200 |
201 | echo "${green}>>> See the Makefile${reset}"
202 | cat Makefile
203 |
204 | echo "${red}>>> Important: Dont add .env in your public repository.${reset}"
205 | echo "${red}>>> KEEP YOUR SECRET_KEY AND PASSWORDS IN SECRET!!!\n${reset}"
206 | echo "${green}>>> Done${reset}"
207 |
--------------------------------------------------------------------------------
/basic/fixtures.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "model": "contenttypes.contenttype",
4 | "pk": 1,
5 | "fields": {
6 | "app_label": "admin",
7 | "model": "logentry"
8 | }
9 | },
10 | {
11 | "model": "contenttypes.contenttype",
12 | "pk": 2,
13 | "fields": {
14 | "app_label": "auth",
15 | "model": "permission"
16 | }
17 | },
18 | {
19 | "model": "contenttypes.contenttype",
20 | "pk": 3,
21 | "fields": {
22 | "app_label": "auth",
23 | "model": "group"
24 | }
25 | },
26 | {
27 | "model": "contenttypes.contenttype",
28 | "pk": 4,
29 | "fields": {
30 | "app_label": "auth",
31 | "model": "user"
32 | }
33 | },
34 | {
35 | "model": "contenttypes.contenttype",
36 | "pk": 5,
37 | "fields": {
38 | "app_label": "contenttypes",
39 | "model": "contenttype"
40 | }
41 | },
42 | {
43 | "model": "contenttypes.contenttype",
44 | "pk": 6,
45 | "fields": {
46 | "app_label": "sessions",
47 | "model": "session"
48 | }
49 | },
50 | {
51 | "model": "contenttypes.contenttype",
52 | "pk": 7,
53 | "fields": {
54 | "app_label": "core",
55 | "model": "person"
56 | }
57 | },
58 | {
59 | "model": "core.person",
60 | "pk": 1,
61 | "fields": {
62 | "created": "2016-01-05T03:15:38.946Z",
63 | "modified": "2016-01-05T03:15:38.946Z",
64 | "gender": "M",
65 | "treatment": "e",
66 | "first_name": "Jesse",
67 | "last_name": "Goforth",
68 | "cpf": "82741529862",
69 | "birthday": "1995-07-13T19:47:20.342Z",
70 | "email": "j.goforth@example.com",
71 | "phone": "(43) 5173-6513",
72 | "blocked": false
73 | }
74 | },
75 | {
76 | "model": "core.person",
77 | "pk": 2,
78 | "fields": {
79 | "created": "2016-01-05T03:15:39.116Z",
80 | "modified": "2016-01-05T03:15:39.116Z",
81 | "gender": "M",
82 | "treatment": "p",
83 | "first_name": "Johnny",
84 | "last_name": "Goyette",
85 | "cpf": "68718761539",
86 | "birthday": "1925-03-22T18:51:55.440Z",
87 | "email": "j.goyette@example.com",
88 | "phone": "(93) 0650-7803",
89 | "blocked": false
90 | }
91 | },
92 | {
93 | "model": "core.person",
94 | "pk": 3,
95 | "fields": {
96 | "created": "2016-01-05T03:15:39.289Z",
97 | "modified": "2016-01-05T03:15:39.289Z",
98 | "gender": "M",
99 | "treatment": "e",
100 | "first_name": "William",
101 | "last_name": "Stafford",
102 | "cpf": "91105938543",
103 | "birthday": "1991-06-12T12:19:11.052Z",
104 | "email": "w.stafford@example.com",
105 | "phone": "(44) 8731-2792",
106 | "blocked": false
107 | }
108 | },
109 | {
110 | "model": "core.person",
111 | "pk": 4,
112 | "fields": {
113 | "created": "2016-01-05T03:15:39.432Z",
114 | "modified": "2016-01-05T03:15:39.432Z",
115 | "gender": "F",
116 | "treatment": "d",
117 | "first_name": "Yolanda",
118 | "last_name": "Green",
119 | "cpf": "09133449044",
120 | "birthday": "1940-09-13T11:41:44.491Z",
121 | "email": "y.green@example.com",
122 | "phone": "(66) 3799-4812",
123 | "blocked": false
124 | }
125 | },
126 | {
127 | "model": "core.person",
128 | "pk": 5,
129 | "fields": {
130 | "created": "2016-01-05T03:15:39.591Z",
131 | "modified": "2016-01-05T03:15:39.591Z",
132 | "gender": "M",
133 | "treatment": "sr",
134 | "first_name": "Robert",
135 | "last_name": "Bissell",
136 | "cpf": "17626244583",
137 | "birthday": "1994-11-17T09:39:12.227Z",
138 | "email": "r.bissell@example.com",
139 | "phone": "(44) 9207-2780",
140 | "blocked": false
141 | }
142 | },
143 | {
144 | "model": "core.person",
145 | "pk": 6,
146 | "fields": {
147 | "created": "2016-01-05T03:15:39.709Z",
148 | "modified": "2016-01-05T03:15:39.709Z",
149 | "gender": "M",
150 | "treatment": "dr",
151 | "first_name": "Roy",
152 | "last_name": "Thomas",
153 | "cpf": "58152293684",
154 | "birthday": "1962-05-14T16:58:44.476Z",
155 | "email": "r.thomas@example.com",
156 | "phone": "(13) 7623-0619",
157 | "blocked": false
158 | }
159 | },
160 | {
161 | "model": "core.person",
162 | "pk": 7,
163 | "fields": {
164 | "created": "2016-01-05T03:15:39.858Z",
165 | "modified": "2016-01-05T03:15:39.858Z",
166 | "gender": "M",
167 | "treatment": "a",
168 | "first_name": "Edward",
169 | "last_name": "Weeks",
170 | "cpf": "46376850331",
171 | "birthday": "1970-10-07T16:21:38.028Z",
172 | "email": "e.weeks@example.com",
173 | "phone": "(91) 0209-4476",
174 | "blocked": false
175 | }
176 | },
177 | {
178 | "model": "core.person",
179 | "pk": 8,
180 | "fields": {
181 | "created": "2016-01-05T03:15:40.040Z",
182 | "modified": "2016-01-05T03:15:40.040Z",
183 | "gender": "M",
184 | "treatment": "sr",
185 | "first_name": "William",
186 | "last_name": "Chesley",
187 | "cpf": "73042741070",
188 | "birthday": "1935-10-17T21:56:45.652Z",
189 | "email": "w.chesley@example.com",
190 | "phone": "(95) 2894-4929",
191 | "blocked": false
192 | }
193 | },
194 | {
195 | "model": "core.person",
196 | "pk": 9,
197 | "fields": {
198 | "created": "2016-01-05T03:15:40.610Z",
199 | "modified": "2016-01-05T03:15:40.610Z",
200 | "gender": "F",
201 | "treatment": "srta",
202 | "first_name": "Carol",
203 | "last_name": "Ruff",
204 | "cpf": "02549446279",
205 | "birthday": "1938-06-26T14:49:22.720Z",
206 | "email": "c.ruff@example.com",
207 | "phone": "(67) 4622-3861",
208 | "blocked": true
209 | }
210 | },
211 | {
212 | "model": "core.person",
213 | "pk": 10,
214 | "fields": {
215 | "created": "2016-01-05T03:15:40.768Z",
216 | "modified": "2016-01-05T03:15:40.768Z",
217 | "gender": "M",
218 | "treatment": "e",
219 | "first_name": "Jose",
220 | "last_name": "Pisano",
221 | "cpf": "35828715205",
222 | "birthday": "1917-10-30T10:31:48.085Z",
223 | "email": "j.pisano@example.com",
224 | "phone": "(63) 4684-7833",
225 | "blocked": false
226 | }
227 | },
228 | {
229 | "model": "core.person",
230 | "pk": 11,
231 | "fields": {
232 | "created": "2016-01-05T03:15:40.933Z",
233 | "modified": "2016-01-05T03:15:40.933Z",
234 | "gender": "M",
235 | "treatment": "dr",
236 | "first_name": "Grady",
237 | "last_name": "Dillard",
238 | "cpf": "26903556889",
239 | "birthday": "1972-03-14T01:56:13.237Z",
240 | "email": "g.dillard@example.com",
241 | "phone": "(28) 9676-7472",
242 | "blocked": true
243 | }
244 | },
245 | {
246 | "model": "core.person",
247 | "pk": 12,
248 | "fields": {
249 | "created": "2016-01-05T03:15:41.097Z",
250 | "modified": "2016-01-05T03:15:41.097Z",
251 | "gender": "M",
252 | "treatment": "sr",
253 | "first_name": "Rodney",
254 | "last_name": "Bishop",
255 | "cpf": "76191023439",
256 | "birthday": "1953-02-04T06:35:43.421Z",
257 | "email": "r.bishop@example.com",
258 | "phone": "(67) 6114-1509",
259 | "blocked": false
260 | }
261 | },
262 | {
263 | "model": "core.person",
264 | "pk": 13,
265 | "fields": {
266 | "created": "2016-01-05T03:15:41.314Z",
267 | "modified": "2016-01-05T03:15:41.314Z",
268 | "gender": "F",
269 | "treatment": "pa",
270 | "first_name": "Kimberly",
271 | "last_name": "Widrick",
272 | "cpf": "56579138743",
273 | "birthday": "1970-11-14T10:53:17.641Z",
274 | "email": "k.widrick@example.com",
275 | "phone": "(87) 5190-3716",
276 | "blocked": false
277 | }
278 | },
279 | {
280 | "model": "core.person",
281 | "pk": 14,
282 | "fields": {
283 | "created": "2016-01-05T03:15:41.464Z",
284 | "modified": "2016-01-05T03:15:41.464Z",
285 | "gender": "M",
286 | "treatment": "e",
287 | "first_name": "Roger",
288 | "last_name": "Mills",
289 | "cpf": "89049893340",
290 | "birthday": "1931-10-05T03:12:01.327Z",
291 | "email": "r.mills@example.com",
292 | "phone": "(22) 5993-3306",
293 | "blocked": false
294 | }
295 | },
296 | {
297 | "model": "core.person",
298 | "pk": 15,
299 | "fields": {
300 | "created": "2016-01-05T03:15:41.622Z",
301 | "modified": "2016-01-05T03:15:41.622Z",
302 | "gender": "F",
303 | "treatment": "srta",
304 | "first_name": "Julie",
305 | "last_name": "Hutchins",
306 | "cpf": "54249454300",
307 | "birthday": "1991-02-26T12:07:20.460Z",
308 | "email": "j.hutchins@example.com",
309 | "phone": "(71) 9075-0587",
310 | "blocked": false
311 | }
312 | },
313 | {
314 | "model": "core.person",
315 | "pk": 16,
316 | "fields": {
317 | "created": "2016-01-05T03:15:41.799Z",
318 | "modified": "2016-01-05T03:15:41.799Z",
319 | "gender": "M",
320 | "treatment": "p",
321 | "first_name": "Salvatore",
322 | "last_name": "Baker",
323 | "cpf": "44861533064",
324 | "birthday": "1926-01-07T22:59:00.457Z",
325 | "email": "s.baker@example.com",
326 | "phone": "(53) 2243-0147",
327 | "blocked": true
328 | }
329 | },
330 | {
331 | "model": "core.person",
332 | "pk": 17,
333 | "fields": {
334 | "created": "2016-01-05T03:15:41.960Z",
335 | "modified": "2016-01-05T03:15:41.960Z",
336 | "gender": "F",
337 | "treatment": "d",
338 | "first_name": "Carmen",
339 | "last_name": "Calloway",
340 | "cpf": "16976738352",
341 | "birthday": "1952-05-16T14:59:44.986Z",
342 | "email": "c.calloway@example.com",
343 | "phone": "(71) 6221-0056",
344 | "blocked": true
345 | }
346 | },
347 | {
348 | "model": "core.person",
349 | "pk": 18,
350 | "fields": {
351 | "created": "2016-01-05T03:15:42.139Z",
352 | "modified": "2016-01-05T03:15:42.139Z",
353 | "gender": "F",
354 | "treatment": "pa",
355 | "first_name": "Lori",
356 | "last_name": "Hamrick",
357 | "cpf": "92625907521",
358 | "birthday": "1980-08-31T15:06:19.670Z",
359 | "email": "l.hamrick@example.com",
360 | "phone": "(50) 8609-3097",
361 | "blocked": true
362 | }
363 | },
364 | {
365 | "model": "core.person",
366 | "pk": 19,
367 | "fields": {
368 | "created": "2016-01-05T03:15:42.350Z",
369 | "modified": "2016-01-05T03:15:42.350Z",
370 | "gender": "F",
371 | "treatment": "sra",
372 | "first_name": "Marguerite",
373 | "last_name": "Kabel",
374 | "cpf": "22332080357",
375 | "birthday": "1943-11-02T16:47:31.089Z",
376 | "email": "m.kabel@example.com",
377 | "phone": "(10) 2213-7157",
378 | "blocked": false
379 | }
380 | },
381 | {
382 | "model": "core.person",
383 | "pk": 20,
384 | "fields": {
385 | "created": "2016-01-05T03:15:42.610Z",
386 | "modified": "2016-01-05T03:15:42.610Z",
387 | "gender": "F",
388 | "treatment": "srta",
389 | "first_name": "Diane",
390 | "last_name": "Deleon",
391 | "cpf": "84444913564",
392 | "birthday": "1916-10-03T10:33:59.872Z",
393 | "email": "d.deleon@example.com",
394 | "phone": "(85) 1887-1200",
395 | "blocked": false
396 | }
397 | },
398 | {
399 | "model": "auth.permission",
400 | "pk": 1,
401 | "fields": {
402 | "name": "Can add log entry",
403 | "content_type": 1,
404 | "codename": "add_logentry"
405 | }
406 | },
407 | {
408 | "model": "auth.permission",
409 | "pk": 2,
410 | "fields": {
411 | "name": "Can change log entry",
412 | "content_type": 1,
413 | "codename": "change_logentry"
414 | }
415 | },
416 | {
417 | "model": "auth.permission",
418 | "pk": 3,
419 | "fields": {
420 | "name": "Can delete log entry",
421 | "content_type": 1,
422 | "codename": "delete_logentry"
423 | }
424 | },
425 | {
426 | "model": "auth.permission",
427 | "pk": 4,
428 | "fields": {
429 | "name": "Can add permission",
430 | "content_type": 2,
431 | "codename": "add_permission"
432 | }
433 | },
434 | {
435 | "model": "auth.permission",
436 | "pk": 5,
437 | "fields": {
438 | "name": "Can change permission",
439 | "content_type": 2,
440 | "codename": "change_permission"
441 | }
442 | },
443 | {
444 | "model": "auth.permission",
445 | "pk": 6,
446 | "fields": {
447 | "name": "Can delete permission",
448 | "content_type": 2,
449 | "codename": "delete_permission"
450 | }
451 | },
452 | {
453 | "model": "auth.permission",
454 | "pk": 7,
455 | "fields": {
456 | "name": "Can add group",
457 | "content_type": 3,
458 | "codename": "add_group"
459 | }
460 | },
461 | {
462 | "model": "auth.permission",
463 | "pk": 8,
464 | "fields": {
465 | "name": "Can change group",
466 | "content_type": 3,
467 | "codename": "change_group"
468 | }
469 | },
470 | {
471 | "model": "auth.permission",
472 | "pk": 9,
473 | "fields": {
474 | "name": "Can delete group",
475 | "content_type": 3,
476 | "codename": "delete_group"
477 | }
478 | },
479 | {
480 | "model": "auth.permission",
481 | "pk": 10,
482 | "fields": {
483 | "name": "Can add user",
484 | "content_type": 4,
485 | "codename": "add_user"
486 | }
487 | },
488 | {
489 | "model": "auth.permission",
490 | "pk": 11,
491 | "fields": {
492 | "name": "Can change user",
493 | "content_type": 4,
494 | "codename": "change_user"
495 | }
496 | },
497 | {
498 | "model": "auth.permission",
499 | "pk": 12,
500 | "fields": {
501 | "name": "Can delete user",
502 | "content_type": 4,
503 | "codename": "delete_user"
504 | }
505 | },
506 | {
507 | "model": "auth.permission",
508 | "pk": 13,
509 | "fields": {
510 | "name": "Can add content type",
511 | "content_type": 5,
512 | "codename": "add_contenttype"
513 | }
514 | },
515 | {
516 | "model": "auth.permission",
517 | "pk": 14,
518 | "fields": {
519 | "name": "Can change content type",
520 | "content_type": 5,
521 | "codename": "change_contenttype"
522 | }
523 | },
524 | {
525 | "model": "auth.permission",
526 | "pk": 15,
527 | "fields": {
528 | "name": "Can delete content type",
529 | "content_type": 5,
530 | "codename": "delete_contenttype"
531 | }
532 | },
533 | {
534 | "model": "auth.permission",
535 | "pk": 16,
536 | "fields": {
537 | "name": "Can add session",
538 | "content_type": 6,
539 | "codename": "add_session"
540 | }
541 | },
542 | {
543 | "model": "auth.permission",
544 | "pk": 17,
545 | "fields": {
546 | "name": "Can change session",
547 | "content_type": 6,
548 | "codename": "change_session"
549 | }
550 | },
551 | {
552 | "model": "auth.permission",
553 | "pk": 18,
554 | "fields": {
555 | "name": "Can delete session",
556 | "content_type": 6,
557 | "codename": "delete_session"
558 | }
559 | },
560 | {
561 | "model": "auth.permission",
562 | "pk": 19,
563 | "fields": {
564 | "name": "Can add contact",
565 | "content_type": 7,
566 | "codename": "add_person"
567 | }
568 | },
569 | {
570 | "model": "auth.permission",
571 | "pk": 20,
572 | "fields": {
573 | "name": "Can change contact",
574 | "content_type": 7,
575 | "codename": "change_person"
576 | }
577 | },
578 | {
579 | "model": "auth.permission",
580 | "pk": 21,
581 | "fields": {
582 | "name": "Can delete contact",
583 | "content_type": 7,
584 | "codename": "delete_person"
585 | }
586 | }
587 | ]
588 |
--------------------------------------------------------------------------------
/setupfull.sh:
--------------------------------------------------------------------------------
1 | # Shell script to create a full Django project.
2 | # The project contains:
3 | # Core App
4 | # Person Model
5 | # Person List
6 | # Person Detail
7 | # Person Form
8 | # Person Admin
9 | # API REST
10 | # Tests with Selenium
11 | # Fixtures with Generate Random Values
12 | # New commands with django-admin
13 |
14 | # Download: wget --output-document=setupfull.sh URL_NAME
15 |
16 | # Usage: source setupfull.sh
17 |
18 | # Colors
19 | red=`tput setaf 1`
20 | green=`tput setaf 2`
21 | reset=`tput sgr0`
22 |
23 | # default name project
24 | echo "Type the name of project (default: myproject): "
25 | echo "Digite o nome do projeto (default: myproject): "
26 | read p
27 | PROJECT=${p-myproject}
28 |
29 | echo "${green}>>> Remove .venv${reset}"
30 | rm -rf .venv
31 |
32 | echo "${green}>>> Remove djangoproject${reset}"
33 | rm -rf djangoproject
34 |
35 | echo "${green}>>> Creating djangoproject.${reset}"
36 | mkdir djangoproject
37 | cd djangoproject
38 |
39 | echo "${green}>>> Creating virtualenv${reset}"
40 | virtualenv -p python3 .venv
41 | echo "${green}>>> venv is created.${reset}"
42 |
43 | # active
44 | sleep 2
45 | echo "${green}>>> activate the venv.${reset}"
46 | source .venv/bin/activate
47 |
48 | echo "${green}>>> Short the prompt path.${reset}"
49 | PS1="(`basename \"$VIRTUAL_ENV\"`)\e[1;34m:/\W\e[00m$ "
50 | sleep 2
51 |
52 | # installdjango
53 | echo "${green}>>> Installing the Django${reset}"
54 | pip install django djangorestframework django-bootstrap-form django-daterange-filter selenium names rstr pytz
55 | pip freeze > requirements.txt
56 |
57 | # createproject
58 | echo "${green}>>> Creating the project '$PROJECT' ...${reset}"
59 | django-admin.py startproject $PROJECT .
60 | cd $PROJECT
61 | echo "${green}>>> Creating the app 'core' ...${reset}"
62 | python ../manage.py startapp core
63 | cd ..
64 |
65 | # migrate
66 | python manage.py migrate
67 |
68 | # createuser
69 | echo "${green}>>> Creating a 'admin' user ...${reset}"
70 | echo "${green}>>> The password must contain at least 8 characters.${reset}"
71 | echo "${green}>>> Password suggestions: demodemo${reset}"
72 | python manage.py createsuperuser --username='admin' --email=''
73 |
74 |
75 | echo "${green}>>> Editing settings.py${reset}"
76 | sed -i "/django.contrib.staticfiles/a\ '$PROJECT.core'," $PROJECT/settings.py
77 |
78 |
79 | echo "${green}>>> Editing urls.py${reset}"
80 | cat << 'EOF' > $PROJECT/urls.py
81 | from django.conf.urls import url
82 | from django.contrib import admin
83 | import PROJECT.core.views as v
84 |
85 | urlpatterns = [
86 | url(r'^$', v.home, name='home'),
87 | url(r'^person/$', v.PersonList.as_view(), name='person_list'),
88 | url(r'^person/(?P\d+)/$', v.PersonDetail.as_view(), name='person_detail'),
89 | url(r'^person/add/$', v.PersonCreate.as_view(), name='person_add'),
90 | url(r'^admin/', admin.site.urls),
91 | ]
92 | EOF
93 |
94 | # Editing urls.py
95 | sed -i "s/PROJECT/$PROJECT/" $PROJECT/urls.py
96 |
97 | echo "${green}>>> Editing views.py${reset}"
98 | cat << 'EOF' > $PROJECT/core/views.py
99 | from django.shortcuts import render
100 | from django.http import HttpResponse
101 | from django.core.urlresolvers import reverse_lazy
102 | from django.views.generic import CreateView, TemplateView, ListView, DetailView
103 | from PROJECT.core.models import Person
104 |
105 |
106 | def home(request):
107 | return render(request, 'index.html')
108 |
109 |
110 | class PersonList(ListView):
111 | template_name = 'core/person_list.html'
112 | model = Person
113 | context_object_name = 'persons'
114 |
115 |
116 | class PersonCreate(CreateView):
117 | template_name = 'core/person_form.html'
118 | model = Person
119 | fields = '__all__'
120 | success_url = reverse_lazy('person_list')
121 |
122 |
123 | class PersonDetail(DetailView):
124 | template_name = 'core/person_detail.html'
125 | model = Person
126 | EOF
127 |
128 | # Editing views.py
129 | sed -i "s/PROJECT/$PROJECT/" $PROJECT/core/views.py
130 |
131 |
132 |
133 | echo "${green}>>> Editing models.py${reset}"
134 | cat << 'EOF' > $PROJECT/core/models.py
135 | from django.db import models
136 | from django.core.urlresolvers import reverse_lazy
137 |
138 | gender_list = [('M', 'male'), ('F', 'female')]
139 |
140 | class TimeStampedModel(models.Model):
141 | created = models.DateTimeField(auto_now_add=True, auto_now=False)
142 | modified = models.DateTimeField(auto_now_add=False, auto_now=True)
143 |
144 | class Meta:
145 | abstract = True
146 |
147 |
148 | class Person(TimeStampedModel):
149 | gender = models.CharField(max_length=1, choices=gender_list)
150 | first_name = models.CharField('first name', max_length=30)
151 | last_name = models.CharField('last name', max_length=30)
152 | birthday = models.DateTimeField(null=True, blank=True)
153 | email = models.EmailField(blank=True)
154 | phone = models.CharField(max_length=20, default='')
155 | blocked = models.BooleanField(default=False)
156 |
157 | class Meta:
158 | ordering = ['first_name']
159 | verbose_name = 'contact'
160 | verbose_name_plural = 'contacts'
161 |
162 | def __str__(self):
163 | return "{} {}".format(self.first_name, self.last_name)
164 |
165 | full_name = property(__str__)
166 |
167 | def get_absolute_url(self):
168 | ''' return id '''
169 | return reverse_lazy('person_detail', kwargs={'pk': self.pk})
170 | EOF
171 |
172 |
173 | echo "${green}>>> Editing admin.py${reset}"
174 | cat << 'EOF' > $PROJECT/core/admin.py
175 | from django.contrib import admin
176 | from .models import Person
177 |
178 | admin.site.register(Person)
179 | EOF
180 |
181 |
182 | echo "${green}>>> Editing forms.py${reset}"
183 | cat << 'EOF' > $PROJECT/core/forms.py
184 | from django import forms
185 | from .models import Person
186 |
187 | gender_list = [('M', 'male'), ('F', 'female')]
188 |
189 |
190 | class PersonForm(forms.ModelForm):
191 | gender = forms.ChoiceField(
192 | choices=gender_list, initial='M', widget=forms.RadioSelect)
193 |
194 | class Meta:
195 | model = Person
196 | fields = '__all__'
197 |
198 | def clean_cpf(self):
199 | return self.cleaned_data['cpf'] or None
200 | EOF
201 |
202 |
203 | echo "${green}>>> Creating templates${reset}"
204 | mkdir -p $PROJECT/core/templates/core
205 | touch $PROJECT/core/templates/{base.html,menu.html,index.html,pagination.html}
206 | touch $PROJECT/core/templates/core/{person_list.html,person_detail.html,person_form.html}
207 |
208 |
209 | echo "${green}>>> Migrating${reset}"
210 | python manage.py makemigrations
211 | python manage.py migrate
212 |
213 | echo "${green}>>> Editing base.html${reset}"
214 |
215 | cat << 'EOF' > $PROJECT/core/templates/base.html
216 |
217 |
218 |
219 | {% load static %}
220 |
221 |
222 |
223 |
224 |
225 |
226 | {% block title %}
227 | Django Example
228 | {% endblock title %}
229 |
230 |
231 |
232 |
233 |
234 |
235 |
242 |
243 |
244 |
245 |
246 | {% include "menu.html" %}
247 |
248 | {% block content %}
249 |
250 | {% endblock content %}
251 |
252 |
253 | EOF
254 |
255 |
256 |
257 | echo "${green}>>> Editing menu.html${reset}"
258 |
259 | cat << 'EOF' > $PROJECT/core/templates/menu.html
260 |
261 |
281 | EOF
282 |
283 |
284 |
285 | echo "${green}>>> Editing index.html${reset}"
286 |
287 | cat << 'EOF' > $PROJECT/core/templates/index.html
288 | {% extends "base.html" %}
289 |
290 | {% block content %}
291 |
292 |
293 |
Contacts
294 |
Contacts list make in Django.
295 |
The project contains:
296 |
297 | - Core App
298 | - Person Model
299 | - Person List
300 | - Person Detail
301 | - Person Form
302 | - Person Admin
303 | - API REST
304 | - Tests with Selenium
305 | - Fixtures with Generate Random Values
306 | - New commands with django-admin
307 |
308 |
309 |
310 | {% endblock content %}
311 | EOF
312 |
313 |
314 |
315 | echo "${green}>>> Editing pagination.html${reset}"
316 |
317 | cat << 'EOF' > $PROJECT/core/templates/pagination.html
318 |
319 |
338 | EOF
339 |
340 |
341 |
342 | echo "${green}>>> Editing person_list.html${reset}"
343 |
344 | cat << 'EOF' > $PROJECT/core/templates/core/person_list.html
345 | {% extends "base.html" %}
346 |
347 | {% block content %}
348 |
349 |
350 |
351 |
354 |
355 | {% if persons %}
356 |
357 |
358 |
359 | | Name |
360 | Email |
361 | Phone |
362 | Birthday |
363 | Blocked |
364 |
365 |
366 |
367 | {% for person in persons %}
368 |
369 | | {{ person.full_name }} |
370 | {{ person.email }} |
371 | {{ person.phone }} |
372 | {{ person.birthday|date:"d/m/Y" }} |
373 | {% if person.blocked %}
374 | |
375 | {% else %}
376 | |
377 | {% endif %}
378 |
379 | {% endfor %}
380 |
381 |
382 | {% else %}
383 |
Without items in this list.
384 | {% endif %}
385 |
386 | {% endblock content %}
387 | EOF
388 |
389 |
390 | echo "${green}>>> Editing person_detail.html${reset}"
391 |
392 | cat << 'EOF' > $PROJECT/core/templates/core/person_detail.html
393 | {% extends "base.html" %}
394 |
395 | {% load static %}
396 |
397 | {% block title %}
398 | Person Detail
399 | {% endblock title %}
400 |
401 | {% block content %}
402 |
403 |
411 |
412 |
413 |
414 |

415 |
416 |
417 | {% if object.treatment %}
418 |
{{ object.get_treatment_display }} {{ object.full_name }}
419 | {% else %}
420 |
{{ object.full_name }}
421 | {% endif %}
422 | {% if object.email %}
423 |
424 | {% endif %}
425 |
426 |
427 |
428 |
429 |
556 |
557 |
558 | {% endblock content %}
559 | EOF
560 |
561 |
562 | echo "${green}>>> Editing person_form.html${reset}"
563 |
564 | cat << 'EOF' > $PROJECT/core/templates/core/person_form.html
565 |
566 | EOF
567 |
568 |
569 |
570 | # Person List
571 | # Person Detail
572 | # Person Form
573 | # Person Admin
574 | # API REST
575 | # Tests with Selenium
576 | # Fixtures with Generate Random Values
577 | # New commands with django-admin
578 | # Gráficos
--------------------------------------------------------------------------------