├── littlelemon ├── littlelemon │ ├── __init__.py │ ├── asgi.py │ ├── wsgi.py │ ├── urls.py │ └── settings.py ├── restaurant │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0002_alter_menu_inventory.py │ │ └── 0001_initial.py │ ├── throttles.py │ ├── admin.py │ ├── apps.py │ ├── urls.py │ ├── serializers.py │ ├── models.py │ └── views.py ├── static │ ├── img │ │ ├── logo.png │ │ ├── Grill B.jpg │ │ ├── Grill.jpg │ │ ├── favicon.ico │ │ ├── pasta.jpg │ │ ├── salad.jpg │ │ ├── head_chef.jpg │ │ ├── Bruschetta.jpg │ │ ├── Grilled fish.jpg │ │ ├── greek salad.jpg │ │ ├── logo_footer.png │ │ ├── Grilled fish B.jpg │ │ ├── Grilled fish C.jpg │ │ ├── lemon dessert.jpg │ │ ├── lemon dessert B.jpg │ │ ├── mario-and-adrian.jpg │ │ ├── restaurant chef C.jpg │ │ ├── restaurant food B.jpg │ │ ├── restaurant food.jpg │ │ ├── restaurant_inside.jpg │ │ ├── Mario and Adrian b.jpg │ │ └── restaurant inside alternative.jpg │ └── css │ │ └── style.css ├── tests │ ├── test_models.py │ └── test_views.py ├── templates │ └── index.html └── manage.py ├── newfile.txt ├── README.md └── .gitignore /littlelemon/littlelemon/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /littlelemon/restaurant/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /newfile.txt: -------------------------------------------------------------------------------- 1 | Hello World, it's semilogo 2 | -------------------------------------------------------------------------------- /littlelemon/restaurant/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /littlelemon/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/logo.png -------------------------------------------------------------------------------- /littlelemon/static/img/Grill B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/Grill B.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/Grill.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/Grill.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/favicon.ico -------------------------------------------------------------------------------- /littlelemon/static/img/pasta.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/pasta.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/salad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/salad.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/head_chef.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/head_chef.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/Bruschetta.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/Bruschetta.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/Grilled fish.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/Grilled fish.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/greek salad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/greek salad.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/logo_footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/logo_footer.png -------------------------------------------------------------------------------- /littlelemon/static/img/Grilled fish B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/Grilled fish B.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/Grilled fish C.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/Grilled fish C.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/lemon dessert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/lemon dessert.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/lemon dessert B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/lemon dessert B.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/mario-and-adrian.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/mario-and-adrian.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/restaurant chef C.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/restaurant chef C.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/restaurant food B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/restaurant food B.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/restaurant food.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/restaurant food.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/restaurant_inside.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/restaurant_inside.jpg -------------------------------------------------------------------------------- /littlelemon/static/img/Mario and Adrian b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/Mario and Adrian b.jpg -------------------------------------------------------------------------------- /littlelemon/restaurant/throttles.py: -------------------------------------------------------------------------------- 1 | from rest_framework.throttling import UserRateThrottle 2 | 3 | class TenCallsPerMinute(UserRateThrottle): 4 | rate = '10/minute' -------------------------------------------------------------------------------- /littlelemon/static/img/restaurant inside alternative.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semilogopaul/littlelemon/HEAD/littlelemon/static/img/restaurant inside alternative.jpg -------------------------------------------------------------------------------- /littlelemon/restaurant/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Booking, Menu 3 | # Register your models here. 4 | admin.site.register(Booking) 5 | admin.site.register(Menu) 6 | -------------------------------------------------------------------------------- /littlelemon/restaurant/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class RestaurantConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'restaurant' 7 | -------------------------------------------------------------------------------- /littlelemon/restaurant/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import MenuItemView, SingleMenuItemView, index 3 | 4 | urlpatterns = [ 5 | path('', index, name='index'), 6 | path('menu/', MenuItemView.as_view()), 7 | path('menu/', SingleMenuItemView.as_view()), 8 | ] 9 | -------------------------------------------------------------------------------- /littlelemon/tests/test_models.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from restaurant.models import Menu 3 | class MenuTest(TestCase): 4 | def test_add_item(self): 5 | item = Menu.objects.create(id=3, title="IceCream", price=80, inventory=100) 6 | self.assertEqual(item, "IceCream : 80") -------------------------------------------------------------------------------- /littlelemon/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Capstone Project 4 | 5 | 6 |

Welcome To LittleLemon Restaurant

7 | 8 | {% load static %} 9 | 10 | 11 | -------------------------------------------------------------------------------- /littlelemon/restaurant/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from .models import Booking, Menu 3 | 4 | class bookingSerializer(serializers.ModelSerializer): 5 | class Meta: 6 | model = Booking 7 | fields = '__all__' 8 | 9 | class menuSerializer(serializers.ModelSerializer): 10 | class Meta: 11 | model = Menu 12 | fields = '__all__' -------------------------------------------------------------------------------- /littlelemon/littlelemon/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for littlelemon 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/5.0/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', 'littlelemon.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /littlelemon/littlelemon/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for littlelemon 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/5.0/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', 'littlelemon.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /littlelemon/restaurant/migrations/0002_alter_menu_inventory.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.7 on 2024-07-26 16:50 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('restaurant', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='menu', 15 | name='Inventory', 16 | field=models.IntegerField(max_length=5, unique=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /littlelemon/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', 'littlelemon.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 | -------------------------------------------------------------------------------- /littlelemon/restaurant/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | class Booking(models.Model): 5 | ID = models.IntegerField(primary_key=True, unique=True, max_length=11) 6 | Name = models.CharField(max_length=255) 7 | No_of_guests = models.IntegerField(max_length=6) 8 | BookingDate = models.DateTimeField() 9 | def __str__(self): 10 | return self.Name 11 | 12 | class Menu(models.Model): 13 | ID = models.IntegerField(primary_key=True, unique=True, max_length=5) 14 | Title = models.CharField(max_length=255) 15 | Price = models.DecimalField(max_digits=10, decimal_places=2) 16 | Inventory = models.IntegerField(unique=True, max_length=5) 17 | def __str__(self): 18 | return f'{self.Title} : {str(self.Price)}' 19 | -------------------------------------------------------------------------------- /littlelemon/tests/test_views.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from rest_framework.test import APIClient 3 | from restaurant.models import Menu 4 | from restaurant.serializers import menuSerializer 5 | 6 | 7 | class MenuViewTest(TestCase): 8 | def test_setUp(self): 9 | self.client = APIClient() 10 | self.menu1 = Menu.objects.create(ID=4, Title='Burger', Price=7, Inventory=9) 11 | self.menu2 = Menu.objects.create(ID=5, Title='Falafel', Price=8, Inventory=15) 12 | self.menu3 = Menu.objects.create(ID=6, Title='Lasagma', Price=9, Inventory=13) 13 | 14 | def test_getall(self): 15 | response = self.client.get('/menu/') 16 | menus = Menu.objects.all() 17 | serializer = menuSerializer(menus, many=True) 18 | self.assertEqual(response.status_code, 200) 19 | self.assertEqual(response.data, serializer.data) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # littlelemon 2 | Developed an API for the LittleLemon Restaurant for reservation management and other functions, implementing token-based authentication, throttling, and pagination to ensure secure and efficient API usage. The API supports multiple response formats, including JSON and XML. Additionally, I integrated the Django Debug Toolbar to facilitate easy debugging. 3 | 4 | **some url routes** 5 | 6 | - http://localhost:8000/auth/users/ 7 | - http://localhost:8000/auth/users/me/ 8 | - http://localhost:8000/auth/users/confirm/ 9 | - http://localhost:8000/auth/users/resend_activation/ 10 | - http://localhost:8000/auth/users/set_password/ 11 | - http://localhost:8000/auth/users/reset_password/ 12 | - http://localhost:8000/auth/users/reset_password_confirm/ 13 | - http://localhost:8000/auth/users/set_username/ 14 | - http://localhost:8000/auth/users/reset_username/ 15 | - http://localhost:8000/auth/users/reset_username_confirm/ 16 | - http://localhost:8000/auth/token/login/ 17 | - http://localhost:8000/auth/token/logout/ 18 | 19 | -------------------------------------------------------------------------------- /littlelemon/restaurant/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.7 on 2024-07-26 16:40 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Booking', 16 | fields=[ 17 | ('ID', models.IntegerField(max_length=11, primary_key=True, serialize=False, unique=True)), 18 | ('Name', models.CharField(max_length=255)), 19 | ('No_of_guests', models.IntegerField(max_length=6)), 20 | ('BookingDate', models.DateTimeField()), 21 | ], 22 | ), 23 | migrations.CreateModel( 24 | name='Menu', 25 | fields=[ 26 | ('ID', models.IntegerField(max_length=5, primary_key=True, serialize=False, unique=True)), 27 | ('Title', models.CharField(max_length=255)), 28 | ('Price', models.DecimalField(decimal_places=2, max_digits=10)), 29 | ('Inventory', models.IntegerField(max_length=5)), 30 | ], 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /littlelemon/littlelemon/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for littlelemon project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/5.0/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | from django.contrib import admin 18 | from django.urls import path, include 19 | from rest_framework.routers import DefaultRouter 20 | from restaurant.views import BookingViewSet 21 | from rest_framework.authtoken.views import obtain_auth_token 22 | import debug_toolbar 23 | 24 | 25 | router = DefaultRouter() 26 | router.register(r'tables', BookingViewSet) 27 | 28 | urlpatterns = [ 29 | path('admin/', admin.site.urls), 30 | path('__debug__/', include(debug_toolbar.urls)), 31 | path('restaurant/menu/', include('restaurant.urls')), 32 | path('restaurant/booking/', include(router.urls)), 33 | path('api-token-auth/', obtain_auth_token, name='api_token_auth'), 34 | path('auth/', include('djoser.urls')), 35 | path('auth/', include('djoser.urls.authtoken')) 36 | 37 | ] 38 | 39 | 40 | -------------------------------------------------------------------------------- /littlelemon/restaurant/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from rest_framework.response import Response 3 | from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView 4 | from rest_framework import viewsets 5 | from rest_framework.permissions import IsAuthenticated 6 | from rest_framework.decorators import permission_classes, throttle_classes 7 | from rest_framework.throttling import AnonRateThrottle, UserRateThrottle 8 | from rest_framework.pagination import PageNumberPagination 9 | from .models import Booking, Menu 10 | from .serializers import bookingSerializer, menuSerializer 11 | from .throttles import TenCallsPerMinute 12 | 13 | # Create your views here. 14 | def index(request): 15 | return render(request, 'index.html', {}) 16 | 17 | # Pagination settings 18 | class StandardResultsSetPagination(PageNumberPagination): 19 | page_size = 10 20 | page_size_query_param = 'page_size' 21 | max_page_size = 100 22 | 23 | # Menu item view with pagination and throttling 24 | class MenuItemView(ListCreateAPIView): 25 | queryset = Menu.objects.all() 26 | serializer_class = menuSerializer 27 | permission_classes = [IsAuthenticated] 28 | pagination_class = StandardResultsSetPagination 29 | throttle_classes = [UserRateThrottle] 30 | 31 | # Single menu item view with throttling 32 | class SingleMenuItemView(RetrieveUpdateDestroyAPIView): 33 | queryset = Menu.objects.all() 34 | serializer_class = menuSerializer 35 | permission_classes = [IsAuthenticated] 36 | throttle_classes = [UserRateThrottle] 37 | 38 | # Booking view set with pagination and custom throttling 39 | class BookingViewSet(viewsets.ModelViewSet): 40 | queryset = Booking.objects.all() 41 | serializer_class = bookingSerializer 42 | permission_classes = [IsAuthenticated] 43 | pagination_class = StandardResultsSetPagination 44 | throttle_classes = [TenCallsPerMinute] # Custom throttle class 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/django 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=django 3 | 4 | ### Django ### 5 | *.log 6 | *.pot 7 | *.pyc 8 | __pycache__/ 9 | local_settings.py 10 | db.sqlite3 11 | db.sqlite3-journal 12 | media 13 | 14 | # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ 15 | # in your Git repository. Update and uncomment the following line accordingly. 16 | # /staticfiles/ 17 | 18 | ### Django.Python Stack ### 19 | # Byte-compiled / optimized / DLL files 20 | *.py[cod] 21 | *$py.class 22 | 23 | # C extensions 24 | *.so 25 | 26 | # Distribution / packaging 27 | .Python 28 | build/ 29 | develop-eggs/ 30 | dist/ 31 | downloads/ 32 | eggs/ 33 | .eggs/ 34 | lib/ 35 | lib64/ 36 | parts/ 37 | sdist/ 38 | var/ 39 | wheels/ 40 | share/python-wheels/ 41 | *.egg-info/ 42 | .installed.cfg 43 | *.egg 44 | MANIFEST 45 | 46 | # PyInstaller 47 | # Usually these files are written by a python script from a template 48 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 49 | *.manifest 50 | *.spec 51 | 52 | # Installer logs 53 | pip-log.txt 54 | pip-delete-this-directory.txt 55 | 56 | # Unit test / coverage reports 57 | htmlcov/ 58 | .tox/ 59 | .nox/ 60 | .coverage 61 | .coverage.* 62 | .cache 63 | nosetests.xml 64 | coverage.xml 65 | *.cover 66 | *.py,cover 67 | .hypothesis/ 68 | .pytest_cache/ 69 | cover/ 70 | 71 | # Translations 72 | *.mo 73 | 74 | # Django stuff: 75 | 76 | # Flask stuff: 77 | instance/ 78 | .webassets-cache 79 | 80 | # Scrapy stuff: 81 | .scrapy 82 | 83 | # Sphinx documentation 84 | docs/_build/ 85 | 86 | # PyBuilder 87 | .pybuilder/ 88 | target/ 89 | 90 | # Jupyter Notebook 91 | .ipynb_checkpoints 92 | 93 | # IPython 94 | profile_default/ 95 | ipython_config.py 96 | 97 | # pyenv 98 | # For a library or package, you might want to ignore these files since the code is 99 | # intended to run in multiple environments; otherwise, check them in: 100 | # .python-version 101 | 102 | # pipenv 103 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 104 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 105 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 106 | # install all needed dependencies. 107 | #Pipfile.lock 108 | 109 | # poetry 110 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 111 | # This is especially recommended for binary packages to ensure reproducibility, and is more 112 | # commonly ignored for libraries. 113 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 114 | #poetry.lock 115 | 116 | # pdm 117 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 118 | #pdm.lock 119 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 120 | # in version control. 121 | # https://pdm.fming.dev/#use-with-ide 122 | .pdm.toml 123 | 124 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 125 | __pypackages__/ 126 | 127 | # Celery stuff 128 | celerybeat-schedule 129 | celerybeat.pid 130 | 131 | # SageMath parsed files 132 | *.sage.py 133 | 134 | # Environments 135 | .env 136 | .venv 137 | env/ 138 | venv/ 139 | ENV/ 140 | env.bak/ 141 | venv.bak/ 142 | 143 | # Spyder project settings 144 | .spyderproject 145 | .spyproject 146 | 147 | # Rope project settings 148 | .ropeproject 149 | 150 | # mkdocs documentation 151 | /site 152 | 153 | # mypy 154 | .mypy_cache/ 155 | .dmypy.json 156 | dmypy.json 157 | 158 | # Pyre type checker 159 | .pyre/ 160 | 161 | # pytype static type analyzer 162 | .pytype/ 163 | 164 | # Cython debug symbols 165 | cython_debug/ 166 | 167 | # PyCharm 168 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 169 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 170 | # and can be added to the global gitignore or merged into this file. For a more nuclear 171 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 172 | #.idea/ 173 | 174 | # End of https://www.toptal.com/developers/gitignore/api/django -------------------------------------------------------------------------------- /littlelemon/static/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Karla', sans-serif; 3 | margin-top: 3rem; 4 | margin-bottom: 3rem; 5 | margin-left: 5%; 6 | margin-right: 5%; 7 | } 8 | 9 | 10 | h1, h2, h3, h4, h5, h6 { 11 | font-family: 'Markazi Text', serif; 12 | letter-spacing: 2px;; 13 | } 14 | 15 | h1 { 16 | font-size: 4rem; 17 | margin-top: 1.5rem; 18 | margin-bottom: 0; 19 | } 20 | 21 | h2 { 22 | font-size: 2.5rem; 23 | color: #333333; 24 | margin:1rem 0; 25 | } 26 | 27 | p, ul{ 28 | font-size: 1.25rem;; 29 | } 30 | 31 | a { 32 | color: #EE9972; 33 | text-decoration: none; 34 | } 35 | 36 | a:hover { 37 | text-decoration: underline; 38 | } 39 | 40 | header img { 41 | display: block; 42 | margin-left: auto; 43 | margin-right: auto; 44 | max-width: 100%; 45 | height: auto; 46 | } 47 | 48 | 49 | nav { 50 | background-color: #495E57; 51 | margin-top: 1rem; 52 | margin-bottom: 1rem; 53 | margin-bottom: 0px; 54 | } 55 | 56 | nav ul { 57 | list-style: none; 58 | font-size: 1.5rem; 59 | text-align: center; 60 | padding-inline-start: 0rem; 61 | margin-bottom: 0px; 62 | } 63 | 64 | nav ul li { 65 | display: inline-block; 66 | margin: 0.5rem; 67 | } 68 | 69 | nav ul a { 70 | color: #EDEFEE; 71 | text-decoration: none; 72 | } 73 | 74 | nav ul a:active { 75 | text-decoration: underline; 76 | } 77 | 78 | nav ul a:hover { 79 | text-decoration: underline; 80 | text-decoration-thickness: 0.1rem; 81 | } 82 | 83 | section { 84 | display: flex; 85 | gap: 2rem 1rem; 86 | margin-top: 1rem; 87 | margin-bottom: 1rem; 88 | flex-direction: row; 89 | } 90 | 91 | article { 92 | flex:1; 93 | } 94 | 95 | article img { 96 | max-width: 100%; 97 | } 98 | 99 | article ul { 100 | list-style: none; 101 | } 102 | 103 | main article { 104 | border: 1px solid #EDEFEE; 105 | padding-left: 1rem; 106 | padding-right: 1rem; 107 | } 108 | 109 | figure { 110 | margin: 0; 111 | } 112 | 113 | label { 114 | font-weight: bold; 115 | display: block; 116 | } 117 | 118 | input{ 119 | width: 80%; 120 | display: block; 121 | padding: .5rem 1rem; 122 | line-height: 1.5rem; 123 | box-sizing: border-box; 124 | } 125 | 126 | #button { 127 | padding: .5rem 1rem; 128 | border: 1px solid #495E57; 129 | background-color:#495E57;; 130 | border-radius: 5px; 131 | width: 80%; 132 | font-size: 1.25rem; 133 | display: block; 134 | color: #fff; 135 | line-height: 25px; 136 | } 137 | 138 | #hero { 139 | background-image: linear-gradient(0deg, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.8)), url('../img/restaurant_inside.jpg'); 140 | background-size: cover; 141 | background-position: 75%; 142 | color: white; 143 | min-height: 200px; 144 | text-align: center;; 145 | } 146 | 147 | #hero p { 148 | font-size: 2rem; 149 | margin:0; 150 | text-shadow: 2px 2px 4px #333333; 151 | } 152 | 153 | .cta { 154 | display: inline-block; 155 | color: rgba(255, 255, 255, 0.9); 156 | width: 200px; 157 | font-weight: bold; 158 | font-size: 1.5rem; 159 | padding: 8px; 160 | border: 3px solid #fff; 161 | text-align: center; 162 | outline: none; 163 | text-decoration: none; 164 | margin: 1rem; 165 | } 166 | 167 | .cta:hover { 168 | color:#fff; 169 | background:#495E57; 170 | border: 3px solid #495E57; 171 | } 172 | 173 | .heading-shadow { 174 | text-shadow: 1px 1px 4px #333333; 175 | } 176 | 177 | footer { 178 | background-color: #EDEFEE; 179 | padding: 1rem; 180 | display: flex; 181 | gap: 2rem 1rem; 182 | } 183 | 184 | footer img { 185 | max-width: 50%; 186 | } 187 | 188 | footer p { 189 | border-top: #333333; 190 | border-style: solid; 191 | border-width: 0.1rem; 192 | border-bottom: none; 193 | border-left: none; 194 | border-right: none; 195 | text-align: right; 196 | padding-top: 1rem;; 197 | } 198 | 199 | .row { 200 | display: flex; 201 | flex-direction: row; 202 | width: 100%; 203 | padding-bottom: 1rem; 204 | } 205 | 206 | .column { 207 | flex-basis: 50%; 208 | margin-bottom: 1rem; 209 | padding-bottom: 1rem; 210 | } 211 | 212 | .column figure { 213 | padding: 0px 12px; 214 | 215 | } 216 | 217 | 218 | .videowrap { 219 | position: relative; 220 | padding-bottom: 56.25%; /* 16:9 */ 221 | padding-top: 1rem; 222 | height: 0; 223 | } 224 | .videowrap iframe { 225 | position: absolute; 226 | margin-top: 2rem; 227 | top: 0; 228 | left: 0; 229 | width: 100%; 230 | height: 100%; 231 | } 232 | 233 | .menu-price{ 234 | display: block; 235 | font-size: 1rem; 236 | } 237 | 238 | @media (max-width: 800px) { 239 | section { 240 | flex-direction: column; 241 | } 242 | 243 | .row { 244 | flex-direction: column; 245 | width: 100%; 246 | } 247 | 248 | input{ 249 | width: 100%; 250 | } 251 | 252 | #button{ 253 | width: 100%; 254 | } 255 | } -------------------------------------------------------------------------------- /littlelemon/littlelemon/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for littlelemon project. 3 | 4 | Generated by 'django-admin startproject' using Django 5.0.7. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/5.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/5.0/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'django-insecure-ngt(1u!jvks*wt@kzo!shp^&3i*si)u!^!t1%%d59zn(8*!q(+' 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 | 'rest_framework', 41 | 'djoser', 42 | 'restaurant', 43 | 'rest_framework.authtoken', 44 | 'debug_toolbar' 45 | ] 46 | 47 | MIDDLEWARE = [ 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.messages.middleware.MessageMiddleware', 54 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 55 | 'debug_toolbar.middleware.DebugToolbarMiddleware', 56 | ] 57 | 58 | ROOT_URLCONF = 'littlelemon.urls' 59 | 60 | TEMPLATES = [ 61 | { 62 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 63 | 'DIRS': [BASE_DIR / 'templates/'], 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 = 'littlelemon.wsgi.application' 77 | 78 | 79 | # Database 80 | # https://docs.djangoproject.com/en/5.0/ref/settings/#databases 81 | 82 | DATABASES = { 83 | 'default': { 84 | 'ENGINE': 'django.db.backends.mysql', 85 | 'NAME': 'restaurant', 86 | 'USER': 'root', 87 | 'PASSWORD': 'toor', 88 | 'HOST': '127.0.0.1', 89 | 'PORT': '3306', 90 | 'OPTIONS': { 91 | 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'" 92 | } 93 | } 94 | } 95 | 96 | # DATABASES = { 97 | # 'default': { 98 | # 'ENGINE': 'django.db.backends.sqlite3', 99 | # 'NAME': BASE_DIR / 'db.sqlite3', 100 | # } 101 | # } 102 | 103 | 104 | # Password validation 105 | # https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators 106 | 107 | AUTH_PASSWORD_VALIDATORS = [ 108 | { 109 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 110 | }, 111 | { 112 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 113 | }, 114 | { 115 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 116 | }, 117 | { 118 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 119 | }, 120 | ] 121 | 122 | REST_FRAMEWORK = { 123 | 'DEFAULT_RENDERER_CLASSES': [ 124 | 'rest_framework.renderers.JSONRenderer', 125 | 'rest_framework.renderers.BrowsableAPIRenderer', 126 | 'rest_framework_xml.renderers.XMLRenderer', 127 | ], 128 | 'DEFAULT_AUTHENTICATION_CLASSES' : [ 129 | 'rest_framework_simplejwt.authentication.JWTAuthentication', 130 | 'rest_framework.authentication.TokenAuthentication', 131 | 'rest_framework.authentication.SessionAuthentication', 132 | ], 133 | 'DEFAULT_THROTTLE_CLASSES': [ 134 | 'rest_framework.throttling.UserRateThrottle', 135 | 'rest_framework.throttling.AnonRateThrottle', 136 | ], 137 | 'DEFAULT_THROTTLE_RATES' : { 138 | 'anon':'2/minute', 139 | 'user':'5/minute', 140 | 'ten':'10/minute', 141 | }, 142 | 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 143 | 'PAGE_SIZE': 10, 144 | } 145 | 146 | DJOSER={"USER_ID_FIELD":"username"} 147 | 148 | # Internationalization 149 | # https://docs.djangoproject.com/en/5.0/topics/i18n/ 150 | 151 | LANGUAGE_CODE = 'en-us' 152 | 153 | TIME_ZONE = 'UTC' 154 | 155 | USE_I18N = True 156 | 157 | USE_TZ = True 158 | 159 | 160 | # Static files (CSS, JavaScript, Images) 161 | # https://docs.djangoproject.com/en/5.0/howto/static-files/ 162 | 163 | STATIC_URL = 'static/' 164 | 165 | # Default primary key field type 166 | # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field 167 | 168 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 169 | 170 | INTERNAL_IPS = [ 171 | '127.0.0.1' 172 | ] 173 | --------------------------------------------------------------------------------