├── config ├── users │ ├── forms.py │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── admin.py │ ├── apps.py │ ├── urls.py │ ├── templates │ │ └── users │ │ │ ├── signup.html │ │ │ └── login.html │ └── views.py ├── config │ ├── __init__.py │ ├── asgi.py │ ├── wsgi.py │ ├── urls.py │ └── settings.py ├── photoapp │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ └── 0001_initial.py │ ├── forms.py │ ├── tests.py │ ├── admin.py │ ├── apps.py │ ├── templates │ │ └── photoapp │ │ │ ├── taglist.html │ │ │ ├── list.html │ │ │ ├── create.html │ │ │ ├── update.html │ │ │ ├── delete.html │ │ │ └── detail.html │ ├── models.py │ ├── urls.py │ └── views.py ├── manage.py └── templates │ ├── navbar.html │ └── base.html ├── requirements.txt ├── README.md └── .gitignore /config/users/forms.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/config/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/photoapp/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/users/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/photoapp/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/users/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/photoapp/forms.py: -------------------------------------------------------------------------------- 1 | '''Photo app Forms''' 2 | -------------------------------------------------------------------------------- /config/users/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /config/users/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /config/photoapp/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /config/users/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /config/photoapp/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Photo 3 | 4 | # Register your models here. 5 | admin.site.register(Photo) -------------------------------------------------------------------------------- /config/users/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UsersConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'users' 7 | -------------------------------------------------------------------------------- /config/photoapp/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PhotoappConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'photoapp' 7 | -------------------------------------------------------------------------------- /config/photoapp/templates/photoapp/taglist.html: -------------------------------------------------------------------------------- 1 | {% extends 'photoapp/list.html' %} 2 | 3 | {% block body %} 4 | 5 |
6 |

Photos with the tag {{tag}}

7 |
8 | 9 | {{ block.super }} 10 | 11 | {% endblock body %} -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.3.4 2 | astroid==2.5.6 3 | autopep8==1.5.7 4 | Django==3.2.3 5 | django-crispy-forms==1.11.2 6 | django-taggit==1.4.0 7 | isort==5.8.0 8 | lazy-object-proxy==1.6.0 9 | mccabe==0.6.1 10 | Pillow==8.2.0 11 | pycodestyle==2.7.0 12 | pylint==2.8.2 13 | pytz==2021.1 14 | sqlparse==0.4.1 15 | toml==0.10.2 16 | wrapt==1.12.1 17 | -------------------------------------------------------------------------------- /config/users/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from django.contrib.auth.views import LogoutView 4 | 5 | from .views import SignUpView, CustomLoginView 6 | 7 | app_name = 'user' 8 | 9 | urlpatterns = [ 10 | path('signup/', SignUpView.as_view(), name='signup'), 11 | path('login/', CustomLoginView.as_view(), name='login'), 12 | path('logout/', LogoutView.as_view(), name='logout'), 13 | ] -------------------------------------------------------------------------------- /config/photoapp/templates/photoapp/list.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block body %} 4 | 5 |
6 | {% for photo in photos %} 7 |
8 | 9 | {{photo.title}} 10 | 11 |
12 | {% endfor %} 13 |
14 | 15 | {% endblock body %} 16 | -------------------------------------------------------------------------------- /config/config/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for config project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /config/config/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for config 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/3.2/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', 'config.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /config/photoapp/templates/photoapp/create.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load crispy_forms_tags %} 3 | 4 | {% block body %} 5 |
6 |

Add photo

7 |
8 |
9 |
10 | {% csrf_token %} 11 | {{ form|crispy }} 12 | 13 |
14 |
15 | {% endblock body %} 16 | -------------------------------------------------------------------------------- /config/photoapp/templates/photoapp/update.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load crispy_forms_tags %} 3 | 4 | {% block body %} 5 |
6 |

Edit photo {{photo}}

7 |
8 |
9 |
10 | {% csrf_token %} 11 | {{ form|crispy }} 12 | 13 |
14 |
15 | {% endblock body %} 16 | -------------------------------------------------------------------------------- /config/photoapp/models.py: -------------------------------------------------------------------------------- 1 | '''Photoapp Models''' 2 | 3 | from django.db import models 4 | 5 | from django.contrib.auth import get_user_model 6 | 7 | from taggit.managers import TaggableManager 8 | 9 | class Photo(models.Model): 10 | 11 | title = models.CharField(max_length=45) 12 | 13 | description = models.CharField(max_length=250) 14 | 15 | created = models.DateTimeField(auto_now_add=True) 16 | 17 | image = models.ImageField(upload_to='photos/') 18 | 19 | submitter = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) 20 | 21 | tags = TaggableManager() 22 | 23 | def __str__(self): 24 | return self.title -------------------------------------------------------------------------------- /config/users/templates/users/signup.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load crispy_forms_tags %} 3 | {% block body %} 4 |
5 |
6 |
7 | {% csrf_token %} 8 | {{ form|crispy }} 9 | 10 |
11 |
12 | {% comment %} Already Registered {% endcomment %} 13 |
14 |

15 | Already Registered? 16 | Login 17 |

18 |
19 |
20 | {% endblock body %} 21 | -------------------------------------------------------------------------------- /config/users/templates/users/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load crispy_forms_tags %} 3 | 4 | {% block body %} 5 | 6 |
7 |
8 |
9 | {% csrf_token %} 10 | {{ form|crispy }} 11 | 12 |
13 |
14 | {% comment %} Already Registered {% endcomment %} 15 |
16 |

17 | Don't have an account? 18 | Create account 19 |

20 |
21 |
22 | {% endblock body %} 23 | -------------------------------------------------------------------------------- /config/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /config/photoapp/urls.py: -------------------------------------------------------------------------------- 1 | '''Photoapp URL patterns''' 2 | 3 | from django.urls import path 4 | 5 | from .views import ( 6 | PhotoListView, 7 | PhotoTagListView, 8 | PhotoDetailView, 9 | PhotoCreateView, 10 | PhotoUpdateView, 11 | PhotoDeleteView 12 | ) 13 | 14 | app_name = 'photo' 15 | 16 | urlpatterns = [ 17 | path('', PhotoListView.as_view(), name='list'), 18 | 19 | path('tag//', PhotoTagListView.as_view(), name='tag'), 20 | 21 | path('photo//', PhotoDetailView.as_view(), name='detail'), 22 | 23 | path('photo/create/', PhotoCreateView.as_view(), name='create'), 24 | 25 | path('photo//update/', PhotoUpdateView.as_view(), name='update'), 26 | 27 | path('photo//delete/', PhotoDeleteView.as_view(), name='delete'), 28 | ] 29 | -------------------------------------------------------------------------------- /config/users/views.py: -------------------------------------------------------------------------------- 1 | from django.views.generic import CreateView 2 | 3 | from django.contrib.auth import authenticate, login 4 | 5 | from django.contrib.auth.views import LoginView 6 | 7 | from django.contrib.auth.forms import UserCreationForm 8 | 9 | from django.urls import reverse_lazy 10 | 11 | class SignUpView(CreateView): 12 | 13 | template_name = 'users/signup.html' 14 | 15 | form_class = UserCreationForm 16 | 17 | success_url = reverse_lazy('photo:list') 18 | 19 | def form_valid(self, form): 20 | to_return = super().form_valid(form) 21 | 22 | user = authenticate( 23 | username=form.cleaned_data["username"], 24 | password=form.cleaned_data["password1"], 25 | ) 26 | 27 | login(self.request, user) 28 | 29 | return to_return 30 | 31 | class CustomLoginView(LoginView): 32 | 33 | template_name = 'users/login.html' -------------------------------------------------------------------------------- /config/photoapp/templates/photoapp/delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block body %} 4 |
5 |

6 | You are going to delete: "{{ photo }} 9 | " 10 |

11 |

Are you sure, you want to delete the photo ?

12 |
13 |
18 | {% csrf_token %} 19 |
20 |
21 | Cancel 24 |
25 |
26 | 27 |
28 |
29 |

This action is irreversible

30 |
31 |
32 |
33 | 34 | {% endblock body %} 35 | -------------------------------------------------------------------------------- /config/config/urls.py: -------------------------------------------------------------------------------- 1 | """config URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | 18 | from django.urls import path, include 19 | 20 | from django.conf import settings 21 | from django.conf.urls.static import static 22 | 23 | urlpatterns = [ 24 | path('admin/', admin.site.urls), 25 | # Main app 26 | path('', include('photoapp.urls')), 27 | # Authentication 28 | path('users/', include('users.urls')), 29 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) -------------------------------------------------------------------------------- /config/photoapp/templates/photoapp/detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block body %} 4 |
5 |

{{ photo.title }}

6 |

Uploaded on: {{photo.created}}
By {{photo.submitter.username}}

7 | {% if user == photo.submitter %} 8 |

9 | Update 10 | Delete 11 |

12 | {% endif %} 13 |
14 |
15 |
16 | 17 |
18 |
19 |

More about this photo:

20 |
    21 | {% for tag in photo.tags.all %} 22 |
  • {{tag.name}}
  • 23 | {% endfor %} 24 |
25 |

{{ photo.description }}

26 |
27 |
28 | 29 | {% endblock body %} 30 | -------------------------------------------------------------------------------- /config/templates/navbar.html: -------------------------------------------------------------------------------- 1 | 40 | -------------------------------------------------------------------------------- /config/photoapp/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.3 on 2021-05-16 21:29 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import taggit.managers 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ('taggit', '0003_taggeditem_add_unique_index'), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='Photo', 21 | fields=[ 22 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('title', models.CharField(max_length=45)), 24 | ('description', models.CharField(max_length=250)), 25 | ('created', models.DateTimeField(auto_now_add=True)), 26 | ('image', models.ImageField(upload_to='photos/')), 27 | ('submitter', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 28 | ('tags', taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')), 29 | ], 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /config/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Django Photo Sharing app 8 | 14 | 15 | 21 | 22 | 23 | {% include 'navbar.html' %} 24 | 25 |
26 | {% block body %} 27 | 28 | {% endblock body %} 29 | 30 |
31 | 32 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /config/photoapp/views.py: -------------------------------------------------------------------------------- 1 | '''Photo app generic views''' 2 | 3 | from django.shortcuts import get_object_or_404 4 | 5 | from django.core.exceptions import PermissionDenied 6 | 7 | from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView 8 | 9 | from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin 10 | 11 | from django.urls import reverse_lazy 12 | 13 | from .models import Photo 14 | 15 | class PhotoListView(ListView): 16 | 17 | model = Photo 18 | 19 | template_name = 'photoapp/list.html' 20 | 21 | context_object_name = 'photos' 22 | 23 | 24 | class PhotoTagListView(PhotoListView): 25 | 26 | template_name = 'photoapp/taglist.html' 27 | 28 | # Custom function 29 | def get_tag(self): 30 | return self.kwargs.get('tag') 31 | 32 | def get_queryset(self): 33 | return self.model.objects.filter(tags__slug=self.get_tag()) 34 | 35 | def get_context_data(self, **kwargs): 36 | context = super().get_context_data(**kwargs) 37 | context["tag"] = self.get_tag() 38 | return context 39 | 40 | 41 | class PhotoDetailView(DetailView): 42 | 43 | model = Photo 44 | 45 | template_name = 'photoapp/detail.html' 46 | 47 | context_object_name = 'photo' 48 | 49 | 50 | class PhotoCreateView(LoginRequiredMixin, CreateView): 51 | 52 | model = Photo 53 | 54 | fields = ['title', 'description', 'image', 'tags'] 55 | 56 | template_name = 'photoapp/create.html' 57 | 58 | success_url = reverse_lazy('photo:list') 59 | 60 | def form_valid(self, form): 61 | 62 | form.instance.submitter = self.request.user 63 | 64 | return super().form_valid(form) 65 | 66 | class UserIsSubmitter(UserPassesTestMixin): 67 | 68 | # Custom method 69 | def get_photo(self): 70 | return get_object_or_404(Photo, pk=self.kwargs.get('pk')) 71 | 72 | def test_func(self): 73 | 74 | if self.request.user.is_authenticated: 75 | return self.request.user == self.get_photo().submitter 76 | else: 77 | raise PermissionDenied('Sorry you are not allowed here') 78 | 79 | class PhotoUpdateView(UserIsSubmitter, UpdateView): 80 | 81 | template_name = 'photoapp/update.html' 82 | 83 | model = Photo 84 | 85 | fields = ['title', 'description', 'tags'] 86 | 87 | success_url = reverse_lazy('photo:list') 88 | 89 | class PhotoDeleteView(UserIsSubmitter, DeleteView): 90 | 91 | template_name = 'photoapp/delete.html' 92 | 93 | model = Photo 94 | 95 | success_url = reverse_lazy('photo:list') -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django-photo-app 2 | 3 | Source code for the article [Build a Django full stack Photo-Sharing app](). 4 | 5 | ## Features 6 | 7 | - Django CRUD functionality 8 | 9 | - User authentication 10 | - Login 11 | - Logout 12 | - Sign-up 13 | 14 | - Image Uploads 15 | 16 | - Reused code with Django Template Language 17 | 18 | - Stylized pages with Bootstrap 5 19 | 20 | 21 | ## SetUp 22 | 23 | Clone the repository: 24 | 25 | ```bash 26 | git clone https://github.com/DaniDiazTech/Django-photo-app.git 27 | 28 | cd Django-photo-app/ 29 | ``` 30 | 31 | Create a virtual environment. 32 | 33 | ```bash 34 | python -m venv .venv 35 | ``` 36 | 37 | Activate it 38 | 39 | ```bash 40 | source .venv/bin/activate 41 | ``` 42 | 43 | ![Virtual environment activated](https://uploads.sitepoint.com/wp-content/uploads/2021/06/1622669492venv.png) 44 | 45 | Install all the project dependencies with the `requirements.txt` with the following command. 46 | 47 | ```bash 48 | pip install -r requirements.txt 49 | ``` 50 | 51 | Run the migrations: 52 | 53 | ```bash 54 | python manage.py migrate 55 | ``` 56 | 57 | Run the project. 58 | 59 | ```bash 60 | python manage.py runserver 61 | ``` 62 | 63 | Visit your [localhost](http://localhost:8000/). 64 | 65 | ![Main Page](https://uploads.sitepoint.com/wp-content/uploads/2021/05/1622404676list.png) 66 | 67 | ## Screenshots 68 | 69 | 70 | ![Dashboard](https://camo.githubusercontent.com/b91eb6691fa1cf9f611ba0b203f2e6f3e0c858de72a4374d5307c2005aff511a/68747470733a2f2f75706c6f6164732e73697465706f696e742e636f6d2f77702d636f6e74656e742f75706c6f6164732f323032312f30352f313632323430343637366c6973742e706e67) 71 | 72 | ![Tag Dashboard](https://camo.githubusercontent.com/2c72474471d3074367f0dbbd5a60f652ec56f450186793639d40dc84d360a068/68747470733a2f2f75706c6f6164732e73697465706f696e742e636f6d2f77702d636f6e74656e742f75706c6f6164732f323032312f30352f313632323430343637397461672d6c6973742e706e67) 73 | 74 | ![Detail page](https://camo.githubusercontent.com/9f9726e296539c66c5bab09984a5e3d9e8b9745fa0b0ac061f07e81f3ebd3e69/68747470733a2f2f75706c6f6164732e73697465706f696e742e636f6d2f77702d636f6e74656e742f75706c6f6164732f323032312f30352f3136323234303534303566697273742d70686f746f2e706e67) 75 | 76 | ![Delete page](https://camo.githubusercontent.com/c694125655898730707b4cd0f04c7775401db3e3bea8d035d743b271bf78f2e3/68747470733a2f2f75706c6f6164732e73697465706f696e742e636f6d2f77702d636f6e74656e742f75706c6f6164732f323032312f30352f3136323234313930323164656c6574652e706e67) 77 | 78 | ![Signup page](https://camo.githubusercontent.com/7e78e06bdf833096bc4231740d8a48384042cab423038e08aae7624d6aeeb4e6/68747470733a2f2f75706c6f6164732e73697465706f696e742e636f6d2f77702d636f6e74656e742f75706c6f6164732f323032312f30352f313632323431393439337369676e75702e706e67) 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by Github_automation 2 | # https://github.com/Daniel1404/Github-automation 3 | # Idea folder 4 | 5 | .idea/ 6 | 7 | ### Python template 8 | # Byte-compiled / optimized / DLL files 9 | __pycache__/ 10 | *.py[cod] 11 | *$py.class 12 | 13 | # C extensions 14 | *.so 15 | 16 | # Distribution / packaging 17 | .Python 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | share/python-wheels/ 31 | *.egg-info/ 32 | .installed.cfg 33 | *.egg 34 | MANIFEST 35 | 36 | # PyInstaller 37 | # Usually these files are written by a python script from a template 38 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 39 | *.manifest 40 | *.spec 41 | 42 | # Installer logs 43 | pip-log.txt 44 | pip-delete-this-directory.txt 45 | 46 | # Unit test / coverage reports 47 | htmlcov/ 48 | .tox/ 49 | .nox/ 50 | .coverage 51 | .coverage.* 52 | .cache 53 | nosetests.xml 54 | coverage.xml 55 | *.cover 56 | *.py,cover 57 | .hypothesis/ 58 | .pytest_cache/ 59 | cover/ 60 | 61 | # Translations 62 | *.mo 63 | *.pot 64 | 65 | # Django stuff: 66 | *.log 67 | local_settings.py 68 | db.sqlite3 69 | db.sqlite3-journal 70 | 71 | # Flask stuff: 72 | instance/ 73 | .webassets-cache 74 | 75 | # Scrapy stuff: 76 | .scrapy 77 | 78 | # Sphinx documentation 79 | docs/_build/ 80 | 81 | # PyBuilder 82 | .pybuilder/ 83 | target/ 84 | 85 | # Jupyter Notebook 86 | .ipynb_checkpoints 87 | 88 | # IPython 89 | profile_default/ 90 | ipython_config.py 91 | 92 | # pyenv 93 | # For a library or package, you might want to ignore these files since the code is 94 | # intended to run in multiple environments; otherwise, check them in: 95 | # .python-version 96 | 97 | # pipenv 98 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 99 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 100 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 101 | # install all needed dependencies. 102 | #Pipfile.lock 103 | 104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 105 | __pypackages__/ 106 | 107 | # Celery stuff 108 | celerybeat-schedule 109 | celerybeat.pid 110 | 111 | # SageMath parsed files 112 | *.sage.py 113 | 114 | # Environments 115 | .env 116 | .venv 117 | env/ 118 | venv/ 119 | ENV/ 120 | env.bak/ 121 | venv.bak/ 122 | 123 | # Spyder project settings 124 | .spyderproject 125 | .spyproject 126 | 127 | # Rope project settings 128 | .ropeproject 129 | 130 | # mkdocs documentation 131 | /site 132 | 133 | # mypy 134 | .mypy_cache/ 135 | .dmypy.json 136 | dmypy.json 137 | 138 | # Pyre type checker 139 | .pyre/ 140 | 141 | # pytype static type analyzer 142 | .pytype/ 143 | 144 | # Cython debug symbols 145 | cython_debug/ 146 | 147 | # Media files 148 | media/ 149 | 150 | # Ignore outline 151 | outline.md -------------------------------------------------------------------------------- /config/config/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for config project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2.3. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | from pathlib import Path 13 | 14 | from django.urls.conf import path 15 | 16 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 17 | BASE_DIR = Path(__file__).resolve().parent.parent 18 | 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = 'django-insecure-1=zd0d^_(otiv6=k3wajcsaoxu5wz_p&^ji7)k(x)p8l(w6h5#' 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = True 28 | 29 | ALLOWED_HOSTS = [] 30 | 31 | 32 | # Application definition 33 | 34 | INSTALLED_APPS = [ 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | 42 | # 3rd party apps 43 | 'taggit', 44 | 'crispy_forms', 45 | 46 | # Custom apps 47 | 'photoapp', 48 | 'users', 49 | ] 50 | 51 | CRISPY_TEMPLATE_PACK = 'bootstrap4' 52 | 53 | MIDDLEWARE = [ 54 | 'django.middleware.security.SecurityMiddleware', 55 | 'django.contrib.sessions.middleware.SessionMiddleware', 56 | 'django.middleware.common.CommonMiddleware', 57 | 'django.middleware.csrf.CsrfViewMiddleware', 58 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 59 | 'django.contrib.messages.middleware.MessageMiddleware', 60 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 61 | ] 62 | 63 | ROOT_URLCONF = 'config.urls' 64 | 65 | TEMPLATES = [ 66 | { 67 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 68 | 'DIRS': [BASE_DIR / 'templates'], 69 | 'APP_DIRS': True, 70 | 'OPTIONS': { 71 | 'context_processors': [ 72 | 'django.template.context_processors.debug', 73 | 'django.template.context_processors.request', 74 | 'django.contrib.auth.context_processors.auth', 75 | 'django.contrib.messages.context_processors.messages', 76 | ], 77 | }, 78 | }, 79 | ] 80 | 81 | WSGI_APPLICATION = 'config.wsgi.application' 82 | 83 | 84 | # Database 85 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 86 | 87 | DATABASES = { 88 | 'default': { 89 | 'ENGINE': 'django.db.backends.sqlite3', 90 | 'NAME': BASE_DIR / 'db.sqlite3', 91 | } 92 | } 93 | 94 | 95 | # Password validation 96 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 97 | 98 | AUTH_PASSWORD_VALIDATORS = [ 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 101 | }, 102 | { 103 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 104 | }, 105 | { 106 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 107 | }, 108 | { 109 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 110 | }, 111 | ] 112 | 113 | # Internationalization 114 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 115 | 116 | LANGUAGE_CODE = 'en-us' 117 | 118 | TIME_ZONE = 'UTC' 119 | 120 | USE_I18N = True 121 | 122 | USE_L10N = True 123 | 124 | USE_TZ = True 125 | 126 | 127 | # Django taggit 128 | 129 | TAGGIT_CASE_INSENSITIVE = True 130 | 131 | # Django Authentication 132 | LOGIN_URL = 'user:login' 133 | LOGIN_REDIRECT_URL = 'photo:list' 134 | 135 | LOGOUT_REDIRECT_URL = 'photo:list' 136 | 137 | # Static files (CSS, JavaScript, Images) 138 | # https://docs.djangoproject.com/en/3.2/howto/static-files/ 139 | 140 | STATIC_URL = '/static/' 141 | 142 | # Default primary key field type 143 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 144 | 145 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 146 | 147 | MEDIA_URL = '/media/' 148 | MEDIA_ROOT = BASE_DIR / 'media' 149 | --------------------------------------------------------------------------------