├── .gitignore ├── .idea ├── .gitignore ├── QuotesApp.iml ├── dataSources.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── LICENSE ├── QuotesApp ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── README.md ├── db.sqlite3 ├── manage.py ├── quotes ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_quote_category.py │ ├── 0003_alter_quote_category.py │ ├── 0004_comment_like.py │ ├── 0005_alter_quote_category.py │ └── __init__.py ├── models.py ├── serializers.py ├── static │ └── images │ │ └── favicon.ico ├── templates │ └── quotes │ │ ├── category.html │ │ ├── comment_item.html │ │ ├── index.html │ │ ├── quote_detail.html │ │ └── search.html ├── tests.py ├── urls.py └── views.py ├── readme-images ├── api.png ├── category.png ├── details.png ├── homepage.png └── results.png └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /.env 2 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/QuotesApp.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 29 | -------------------------------------------------------------------------------- /.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | sqlite.xerial 6 | true 7 | true 8 | $PROJECT_DIR$/QuotesApp/settings.py 9 | org.sqlite.JDBC 10 | jdbc:sqlite:$PROJECT_DIR$/db.sqlite3 11 | $ProjectFileDir$ 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 103 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Son Nguyen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /QuotesApp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoangsonww/Django-Quote-Application/572085c3a1d17e6c741bc1694711a92a899b68ab/QuotesApp/__init__.py -------------------------------------------------------------------------------- /QuotesApp/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for QuotesApp 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', 'QuotesApp.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /QuotesApp/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for QuotesApp project. 3 | 4 | Generated by 'django-admin startproject' using Django 5.0. 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 | from decouple import config 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/5.0/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = config('DJANGO_SECRET_KEY') 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = False 28 | 29 | ALLOWED_HOSTS = ['*'] 30 | 31 | CORS_ALLOWED_ORIGINS = ['*'] 32 | 33 | # Application definition 34 | 35 | INSTALLED_APPS = [ 36 | 'django.contrib.admin', 37 | 'django.contrib.auth', 38 | 'django.contrib.contenttypes', 39 | 'django.contrib.sessions', 40 | 'django.contrib.messages', 41 | 'django.contrib.staticfiles', 42 | 'rest_framework', 43 | 'quotes', 44 | ] 45 | 46 | MIDDLEWARE = [ 47 | 'django.middleware.security.SecurityMiddleware', 48 | 'django.contrib.sessions.middleware.SessionMiddleware', 49 | 'django.middleware.common.CommonMiddleware', 50 | 'django.middleware.csrf.CsrfViewMiddleware', 51 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 52 | 'django.contrib.messages.middleware.MessageMiddleware', 53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 54 | ] 55 | 56 | ROOT_URLCONF = 'QuotesApp.urls' 57 | 58 | TEMPLATES = [ 59 | { 60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 61 | 'DIRS': [], 62 | 'APP_DIRS': True, 63 | 'OPTIONS': { 64 | 'context_processors': [ 65 | 'django.template.context_processors.debug', 66 | 'django.template.context_processors.request', 67 | 'django.contrib.auth.context_processors.auth', 68 | 'django.contrib.messages.context_processors.messages', 69 | ], 70 | }, 71 | }, 72 | ] 73 | 74 | WSGI_APPLICATION = 'QuotesApp.wsgi.application' 75 | 76 | ADMIN_SITE_HEADER = "Quotes Application Administration" 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.sqlite3', 85 | 'NAME': BASE_DIR / 'db.sqlite3', 86 | } 87 | } 88 | 89 | 90 | # Password validation 91 | # https://docs.djangoproject.com/en/5.0/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/5.0/topics/i18n/ 111 | 112 | LANGUAGE_CODE = 'en-us' 113 | 114 | TIME_ZONE = 'UTC' 115 | 116 | USE_I18N = True 117 | 118 | USE_TZ = True 119 | 120 | 121 | # Static files (CSS, JavaScript, Images) 122 | # https://docs.djangoproject.com/en/5.0/howto/static-files/ 123 | 124 | STATIC_URL = 'static/' 125 | 126 | # Default primary key field type 127 | # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field 128 | 129 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 130 | -------------------------------------------------------------------------------- /QuotesApp/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path, include 3 | 4 | urlpatterns = [ 5 | path('admin/', admin.site.urls), 6 | path('', include('quotes.urls')), 7 | ] 8 | -------------------------------------------------------------------------------- /QuotesApp/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for QuotesApp 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', 'QuotesApp.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Django Quotes App 2 | 3 | This is a simple Django-powered web application (backend) that displays inspirational quotes, allows users to like, comment, and search for quotes. It includes a user interface for viewing quotes, an admin interface for managing quotes, and a REST API using Django REST Framework for interacting with quotes programmatically. 4 | 5 | ## Table of Contents 6 | - [Live Deployment](#live-deployment) 7 | - [User Interface](#user-interface) 8 | - [Features](#features) 9 | - [Project Structure](#project-structure) 10 | - [How to Run Locally](#how-to-run-locally) 11 | - [User Guide](#user-guide) 12 | - [REST API Usage](#rest-api-usage) 13 | - [Contributing](#contributing) 14 | - [License](#license) 15 | - [Contact](#contact) 16 | 17 | ## Live Deployment 18 | 19 | You can view the live deployment of this project [here](https://django-quote-application.onrender.com). 20 | 21 | The Django backend is deployed on Render, and the UI is composed of HTML and CSS templates, which are served statically. There is no "real frontend" used in this project, so you can create your own frontend using JavaScript frameworks like React, Vue, or Angular. 22 | 23 | ## User Interface 24 | 25 | ### Homepage 26 | 27 |

28 | User Interface 1 29 |

30 | 31 | ### Category Page 32 | 33 |

34 | User Interface 2 35 |

36 | 37 | ### Search Results 38 | 39 |

40 | User Interface 3 41 |

42 | 43 | ### Quote Details 44 | 45 |

46 | User Interface 4 47 |

48 | 49 | ## Features 50 | 51 | * **Quote of the Day:** A random quote is displayed on the homepage. 52 | * **Liking and Commenting:** Authenticated users can like and comment on quotes. 53 | * **Category Filtering:** Filter quotes by category. 54 | * **Search:** Easily search for quotes by keywords. 55 | * **Responsive Design:** Looks great on desktops, tablets, and mobile devices. 56 | * **Admin Interface:** Add, update, and delete quotes using our admin interface. 57 | * **REST API:** Interact with quotes programmatically using our API endpoints. 58 | 59 | **Note:** Only authenticated users can like and comment on quotes. You can create a superuser account using the Django admin interface or use the admin interface to create user accounts to test these features. 60 | 61 | ## Project Structure 62 | 63 | The project consists of the following main components: 64 | 65 | - **`quotes/`:** 66 | - `models.py`: Contains the database models for quotes and categories. 67 | - `views.py`: Defines the views for rendering different pages. 68 | - `urls.py`: Maps URLs to views. 69 | - `templates/quotes/`: Contains HTML templates for rendering pages. 70 | - `category.html`: Displays quotes in a specific category. 71 | - `quote-detail.html`: Shows the details of a quote. 72 | - `index.html`: The main homepage with the quote of the day. 73 | - `search.html`: Displays search results. 74 | - `static/images/`: Contains images used in the project. 75 | - `__init__.py`: Makes the directory a Python package. 76 | - `admin.py`: Registers models for the admin interface. 77 | - `apps.py`: Configuration for the quotes app. 78 | - `tests.py`: Contains test cases for the application. 79 | - `urls.py`: URL patterns for the quotes app. 80 | - `serializers.py`: Serializers for converting model instances to JSON. 81 | - **`QuotesApp/`:** 82 | - `settings.py`: Contains the project settings and configurations. 83 | - `urls.py`: Defines the URL patterns for the entire project. 84 | - `wsgi.py`: WSGI configuration for deployment. 85 | - `asgi.py`: ASGI configuration for deployment. 86 | - `__init__.py`: Makes the directory a Python package. 87 | - **`db.sqlite3`:** The default SQLite database file. 88 | - **`manage.py`:** A command-line utility for interacting with the project. 89 | 90 | ## How to Run Locally 91 | 92 | ### Prerequisites 93 | 94 | * Python (3.7+) 95 | * Django (4.x) 96 | * Django REST Framework 97 | * Virtual Python Environment (recommended) 98 | * Git 99 | 100 | ### Steps 101 | 102 | 1. **Clone the repository:** Clone the repository using the Code button in the repository's main GitHub page. 103 | 104 | 2. **Create a virtual environment (optional):** 105 | 106 | ```bash 107 | python -m venv venv 108 | source venv/bin/activate # On Windows, use `venv\Scripts\activate` 109 | ``` 110 | 111 | 3. **Apply migrations:** 112 | 113 | ```bash 114 | python manage.py makemigrations 115 | python manage.py migrate 116 | ``` 117 | 118 | 4. **Create a superuser (for admin access):** 119 | 120 | ```bash 121 | python manage.py createsuperuser 122 | ``` 123 | 124 | 5. **Start the development server:** 125 | 126 | ```bash 127 | # Run the server 128 | python manage.py runserver 129 | # Go to the admin interface (http://127.0.0.1:8000/admin/) 130 | # Create quotes and add them to different categories 131 | # Go to the homepage to see the quotes (http://127.0.0.1:8000/) 132 | ``` 133 | 134 | **Important**: Remember to change the Django production secret key and set `DEBUG` to `True` in `QuotesApp/settings.py` order to run the server: 135 | ```python 136 | SECRET_KEY = 'your_secret_key' 137 | 138 | DEBUG = True 139 | ``` 140 | 141 | ## User Guide 142 | 143 | ### Homepage: 144 | 145 | * The main page displays a random quote of the day. 146 | * Like and comment on the quote if you're logged in. 147 | * Use the search bar to find specific quotes. 148 | * Use the category filter to see quotes in a specific category. 149 | 150 | ### Searching: 151 | 152 | * Enter your search query in the search bar. 153 | * The results page will show all quotes containing your keywords. 154 | * Click on a quote to view its details and comments. 155 | 156 | ### Categories: 157 | 158 | * Choose a category from the dropdown on the main page. 159 | * You'll see a list of quotes that belong to that category. 160 | 161 | ### Liking and Commenting: 162 | 163 | * If you like a quote, click the "Like" button. You can unlike it by clicking again. 164 | * To comment, type your comment in the text area and click "Submit." 165 | 166 | ### Adding Quotes (Admin): 167 | 168 | * Go to the admin interface (http://127.0.0.1.8000/admin/). 169 | * Log in with the superuser credentials you created earlier. 170 | * Click on "Quotes" and then "Add Quote." 171 | * Fill in the quote text, author, and category. 172 | * Click "Save" to add the quote to the database. 173 | 174 | ## REST API Usage 175 | 176 | The app also includes a REST API built with Django REST Framework. Here are some of the available endpoints: 177 | 178 | | Endpoint | Method | Description | Authentication Required | 179 | |:-----------------------------------|:------:|:-----------------------------------|:-----------------------:| 180 | | `/api/quotes/` | GET | Get a list of all quotes. | No | 181 | | `/api/quotes//` | GET | Get a specific quote by ID. | No | 182 | | `/api/quotes/` | POST | Create a new quote. | Yes | 183 | | `/api/quotes//` | PUT | Update a specific quote. | Yes | 184 | | `/api/quotes//` | DELETE | Delete a specific quote. | Yes | 185 | | `/api/quotes//comments/` | GET | Get comments for a specific quote. | No | 186 | | `/api/quotes//comments/` | POST | Add a comment to a specific quote. | Yes | 187 | 188 | **Example Usage (with curl):** 189 | 190 | ```bash 191 | # Get all quotes 192 | curl http://127.0.0.1:8000/api/quotes/ 193 | 194 | # Get a specific quote 195 | curl http://127.0.0.1:8000/api/quotes/1/ 196 | ``` 197 | 198 | For example, running `curl http://127.0.0.1:800/api/quotes/` will return this output: 199 | 200 | ```json 201 | [{"id":1,"text":"heheheheh","author":"hehe","category":"General","like_set":[]},{"id":2,"text":"ggegegege","author":"gegegeg","category":"Inspirational","like_set":[23]}] 202 | ``` 203 | 204 | You can also go to the API endpoints directly in your browser to see the JSON responses, such as: 205 | 206 |

207 | User Interface 5 208 |

209 | 210 | **Authentication:** 211 | 212 | For endpoints that require authentication, you'll need to include an authorization token (e.g., JWT) in the request headers. You can obtain this token by implementing a login/authentication system in your Django app. 213 | 214 | **API Development:** 215 | 216 | To get started on developing and customizing this app's APIs, follow these steps: 217 | 218 | ```bash 219 | pip install djangorestframework 220 | ``` 221 | 222 | Then, Add DRF to `INSTALLED_APPS` in `settings.py`: 223 | 224 | ```python 225 | # settings.py 226 | INSTALLED_APPS = [ 227 | # ... existing apps 228 | 'rest_framework', 229 | ] 230 | ``` 231 | 232 | ## Contributing 233 | 234 | If you'd like to contribute to this project, please fork the repository and submit a pull request. 235 | 236 | ## License 237 | 238 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 239 | 240 | ## Contact 241 | 242 | If you have any questions about the project or Django (or even the Django REST Framework) in general, feel free to [contact me](mailto:info@movie-verse.com)! I'll be happy to answer any questions you might have (hopefully I'll know the answers to them...) 243 | 244 | --- 245 | 246 | Thank you for visiting! 247 | -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoangsonww/Django-Quote-Application/572085c3a1d17e6c741bc1694711a92a899b68ab/db.sqlite3 -------------------------------------------------------------------------------- /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', 'QuotesApp.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 | -------------------------------------------------------------------------------- /quotes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoangsonww/Django-Quote-Application/572085c3a1d17e6c741bc1694711a92a899b68ab/quotes/__init__.py -------------------------------------------------------------------------------- /quotes/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Quote 3 | 4 | admin.site.site_header = "Quotes Application Administration" 5 | 6 | 7 | class QuoteAdmin(admin.ModelAdmin): 8 | list_display = ('text', 'author', 'category') 9 | search_fields = ('text', 'author', 'category') 10 | list_filter = ('author', 'category') 11 | ordering = ('author',) 12 | 13 | 14 | admin.site.register(Quote, QuoteAdmin) 15 | -------------------------------------------------------------------------------- /quotes/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class QuotesConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'quotes' 7 | -------------------------------------------------------------------------------- /quotes/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-11 02:12 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='Quote', 16 | fields=[ 17 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('text', models.TextField()), 19 | ('author', models.CharField(max_length=100)), 20 | ], 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /quotes/migrations/0002_quote_category.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-11 02:28 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('quotes', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='quote', 15 | name='category', 16 | field=models.CharField(default='General', max_length=50), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /quotes/migrations/0003_alter_quote_category.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-11 02:33 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('quotes', '0002_quote_category'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='quote', 15 | name='category', 16 | field=models.CharField(choices=[('Inspirational', 'Inspirational'), ('Motivational', 'Motivational'), ('General', 'General'), ('Love', 'Love'), ('Humor', 'Humor'), ('Philosophy', 'Philosophy'), ('Science', 'Science'), ('Technology', 'Technology'), ('Programming', 'Programming'), ('Business', 'Business'), ('Leadership', 'Leadership'), ('Success', 'Success'), ('Life', 'Life'), ('Friendship', 'Friendship'), ('Wisdom', 'Wisdom'), ('Education', 'Education'), ('Health', 'Health'), ('Fitness', 'Fitness'), ('Sports', 'Sports'), ('Music', 'Music'), ('Movies', 'Movies'), ('Art', 'Art'), ('Fashion', 'Fashion'), ('Food', 'Food'), ('Travel', 'Travel'), ('Nature', 'Nature'), ('Environment', 'Environment'), ('Politics', 'Politics'), ('Economics', 'Economics'), ('History', 'History'), ('Religion', 'Religion'), ('Spirituality', 'Spirituality'), ('Psychology', 'Psychology'), ('Self-Help', 'Self-Help'), ('Relationships', 'Relationships'), ('Parenting', 'Parenting'), ('Marriage', 'Marriage'), ('Family', 'Family'), ('Children', 'Children'), ('Teens', 'Teens'), ('Seniors', 'Seniors'), ('General', 'General')], default='General', max_length=100), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /quotes/migrations/0004_comment_like.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-11 03:10 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('quotes', '0003_alter_quote_category'), 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Comment', 18 | fields=[ 19 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('text', models.TextField()), 21 | ('created_at', models.DateTimeField(auto_now_add=True)), 22 | ('quote', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='quotes.quote')), 23 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 24 | ], 25 | ), 26 | migrations.CreateModel( 27 | name='Like', 28 | fields=[ 29 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 30 | ('quote', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='quotes.quote')), 31 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 32 | ], 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /quotes/migrations/0005_alter_quote_category.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 5.0.6 on 2024-06-12 03:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('quotes', '0004_comment_like'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='quote', 15 | name='category', 16 | field=models.CharField(choices=[('Inspirational', 'Inspirational'), ('Motivational', 'Motivational'), ('General', 'General'), ('Love', 'Love'), ('Humor', 'Humor'), ('Philosophy', 'Philosophy'), ('Science', 'Science'), ('Technology', 'Technology'), ('Programming', 'Programming'), ('Business', 'Business'), ('Leadership', 'Leadership'), ('Success', 'Success'), ('Life', 'Life'), ('Friendship', 'Friendship'), ('Wisdom', 'Wisdom'), ('Education', 'Education'), ('Health', 'Health'), ('Fitness', 'Fitness'), ('Sports', 'Sports'), ('Music', 'Music'), ('Movies', 'Movies'), ('Art', 'Art'), ('Fashion', 'Fashion'), ('Food', 'Food'), ('Travel', 'Travel'), ('Nature', 'Nature'), ('Environment', 'Environment'), ('Politics', 'Politics'), ('Economics', 'Economics'), ('History', 'History'), ('Religion', 'Religion'), ('Spirituality', 'Spirituality'), ('Psychology', 'Psychology'), ('Self-Help', 'Self-Help'), ('Relationships', 'Relationships'), ('Parenting', 'Parenting'), ('Marriage', 'Marriage'), ('Family', 'Family'), ('Children', 'Children'), ('Teens', 'Teens'), ('Seniors', 'Seniors'), ('General', 'General'), ('Adventure', 'Adventure'), ('Animals', 'Animals'), ('Architecture', 'Architecture'), ('Books', 'Books'), ('Career', 'Career'), ('Comedy', 'Comedy'), ('Creativity', 'Creativity'), ('Culture', 'Culture'), ('Design', 'Design'), ('Dreams', 'Dreams'), ('Entertainment', 'Entertainment'), ('Finance', 'Finance'), ('Gardening', 'Gardening'), ('Happiness', 'Happiness'), ('Hobbies', 'Hobbies'), ('Home', 'Home'), ('Humanity', 'Humanity'), ('Innovation', 'Innovation'), ('Knowledge', 'Knowledge'), ('Language', 'Language'), ('Literature', 'Literature'), ('Mindfulness', 'Mindfulness'), ('Money', 'Money'), ('Motivation', 'Motivation'), ('Peace', 'Peace'), ('Poetry', 'Poetry'), ('Productivity', 'Productivity'), ('Science Fiction', 'Science Fiction'), ('Social Justice', 'Social Justice'), ('Society', 'Society'), ('Sustainability', 'Sustainability'), ('Time', 'Time'), ('Work', 'Work'), ('Activism', 'Activism'), ('Aging', 'Aging'), ('Astronomy', 'Astronomy'), ('Beauty', 'Beauty'), ('Belief', 'Belief'), ('Challenge', 'Challenge'), ('Change', 'Change'), ('Communication', 'Communication'), ('Community', 'Community'), ('Compassion', 'Compassion'), ('Confidence', 'Confidence'), ('Courage', 'Courage'), ('Curiosity', 'Curiosity'), ('Dance', 'Dance'), ('Diversity', 'Diversity'), ('Empathy', 'Empathy'), ('Empowerment', 'Empowerment'), ('Equality', 'Equality'), ('Ethics', 'Ethics'), ('Exploration', 'Exploration'), ('Faith', 'Faith'), ('Fantasy', 'Fantasy'), ('Fear', 'Fear'), ('Forgiveness', 'Forgiveness'), ('Freedom', 'Freedom'), ('Gender', 'Gender'), ('Gratitude', 'Gratitude'), ('Growth', 'Growth'), ('Healing', 'Healing'), ('Hope', 'Hope'), ('Human Rights', 'Human Rights'), ('Identity', 'Identity'), ('Imagination', 'Imagination'), ('Inclusion', 'Inclusion'), ('Independence', 'Independence'), ('Individuality', 'Individuality'), ('Inspiration', 'Inspiration'), ('Integrity', 'Integrity'), ('Justice', 'Justice'), ('Kindness', 'Kindness'), ('Learning', 'Learning'), ('LGBTQ+', 'LGBTQ+'), ('Loneliness', 'Loneliness'), ('Loss', 'Loss'), ('Magic', 'Magic'), ('Meaning', 'Meaning'), ('Memory', 'Memory'), ('Mental Health', 'Mental Health'), ('Minimalism', 'Minimalism'), ('Mythology', 'Mythology'), ('Opportunity', 'Opportunity'), ('Optimism', 'Optimism'), ('Patience', 'Patience'), ('Perspective', 'Perspective'), ('Philanthropy', 'Philanthropy'), ('Photography', 'Photography'), ('Resilience', 'Resilience'), ('Respect', 'Respect'), ('Responsibility', 'Responsibility'), ('Risk', 'Risk'), ('Self-Acceptance', 'Self-Acceptance'), ('Self-Care', 'Self-Care'), ('Self-Discovery', 'Self-Discovery'), ('Self-Esteem', 'Self-Esteem'), ('Self-Love', 'Self-Love'), ('Simplicity', 'Simplicity'), ('Solitude', 'Solitude'), ('Strength', 'Strength'), ('Theater', 'Theater'), ('Trust', 'Trust'), ('Truth', 'Truth'), ('Vulnerability', 'Vulnerability'), ('Wellness', 'Wellness'), ('Wonder', 'Wonder'), ('Youth', 'Youth'), ('Achievement', 'Achievement'), ('Activism', 'Activism'), ('Adventure', 'Adventure'), ('Adversity', 'Adversity'), ('Aging', 'Aging'), ('Ambition', 'Ambition'), ('Animals', 'Animals'), ('Anxiety', 'Anxiety'), ('Architecture', 'Architecture'), ('Art', 'Art'), ('Astronomy', 'Astronomy'), ('Authenticity', 'Authenticity'), ('Beauty', 'Beauty'), ('Belief', 'Belief'), ('Books', 'Books'), ('Bravery', 'Bravery'), ('Buddhism', 'Buddhism'), ('Business', 'Business'), ('Calm', 'Calm'), ('Career', 'Career'), ('Celebration', 'Celebration'), ('Challenge', 'Challenge'), ('Change', 'Change'), ('Childhood', 'Childhood'), ('Christianity', 'Christianity'), ('Cinema', 'Cinema'), ('Comedy', 'Comedy'), ('Communication', 'Communication'), ('Community', 'Community'), ('Compassion', 'Compassion'), ('Confidence', 'Confidence'), ('Consciousness', 'Consciousness'), ('Contentment', 'Contentment'), ('Courage', 'Courage'), ('Creativity', 'Creativity'), ('Criticism', 'Criticism'), ('Culture', 'Culture'), ('Curiosity', 'Curiosity'), ('Dance', 'Dance'), ('Death', 'Death'), ('Depression', 'Depression'), ('Design', 'Design'), ('Determination', 'Determination'), ('Diversity', 'Diversity'), ('Dreams', 'Dreams'), ('Economics', 'Economics'), ('Education', 'Education'), ('Empathy', 'Empathy'), ('Empowerment', 'Empowerment'), ('Energy', 'Energy'), ('Entertainment', 'Entertainment'), ('Environment', 'Environment'), ('Equality', 'Equality'), ('Ethics', 'Ethics'), ('Exploration', 'Exploration'), ('Failure', 'Failure'), ('Faith', 'Faith'), ('Family', 'Family'), ('Fantasy', 'Fantasy'), ('Fashion', 'Fashion'), ('Fear', 'Fear'), ('Feminism', 'Feminism'), ('Film', 'Film'), ('Finance', 'Finance'), ('Fitness', 'Fitness'), ('Food', 'Food'), ('Forgiveness', 'Forgiveness'), ('Freedom', 'Freedom'), ('Friendship', 'Friendship'), ('Gender', 'Gender'), ('Gratitude', 'Gratitude'), ('Grief', 'Grief'), ('Growth', 'Growth'), ('Happiness', 'Happiness'), ('Healing', 'Healing'), ('Health', 'Health'), ('Hinduism', 'Hinduism'), ('History', 'History'), ('Hobbies', 'Hobbies'), ('Home', 'Home'), ('Honesty', 'Honesty'), ('Hope', 'Hope'), ('Humanity', 'Humanity'), ('Humor', 'Humor'), ('Identity', 'Identity'), ('Imagination', 'Imagination'), ('Inclusion', 'Inclusion'), ('Independence', 'Independence'), ('Individuality', 'Individuality'), ('Innovation', 'Innovation'), ('Inspiration', 'Inspiration'), ('Integrity', 'Integrity'), ('Islam', 'Islam'), ('Judaism', 'Judaism'), ('Justice', 'Justice'), ('Kindness', 'Kindness'), ('Knowledge', 'Knowledge'), ('Language', 'Language'), ('Leadership', 'Leadership'), ('Learning', 'Learning'), ('Life', 'Life'), ('Literature', 'Literature'), ('Loneliness', 'Loneliness'), ('Loss', 'Loss'), ('Love', 'Love'), ('Magic', 'Magic'), ('Marriage', 'Marriage'), ('Meaning', 'Meaning'), ('Memory', 'Memory'), ('Minimalism', 'Minimalism'), ('Mindfulness', 'Mindfulness'), ('Money', 'Money'), ('Motivation', 'Motivation'), ('Music', 'Music'), ('Mystery', 'Mystery'), ('Mythology', 'Mythology'), ('Nature', 'Nature'), ('Opportunity', 'Opportunity'), ('Optimism', 'Optimism'), ('Parenting', 'Parenting'), ('Patience', 'Patience'), ('Peace', 'Peace'), ('Perseverance', 'Perseverance'), ('Perspective', 'Perspective'), ('Philanthropy', 'Philanthropy'), ('Philosophy', 'Philosophy'), ('Photography', 'Photography'), ('Poetry', 'Poetry'), ('Politics', 'Politics'), ('Positivity', 'Positivity'), ('Poverty', 'Poverty'), ('Power', 'Power'), ('Productivity', 'Productivity'), ('Programming', 'Programming'), ('Psychology', 'Psychology'), ('Racism', 'Racism'), ('Relationships', 'Relationships'), ('Religion', 'Religion'), ('Resilience', 'Resilience'), ('Respect', 'Respect'), ('Responsibility', 'Responsibility'), ('Risk', 'Risk'), ('Science', 'Science'), ('Self-Acceptance', 'Self-Acceptance'), ('Self-Care', 'Self-Care'), ('Self-Discovery', 'Self-Discovery'), ('Self-Esteem', 'Self-Esteem'), ('Self-Love', 'Self-Love'), ('Sexuality', 'Sexuality'), ('Simplicity', 'Simplicity'), ('Society', 'Society'), ('Solitude', 'Solitude'), ('Spirituality', 'Spirituality'), ('Sports', 'Sports'), ('Strength', 'Strength'), ('Success', 'Success'), ('Sustainability', 'Sustainability'), ('Technology', 'Technology'), ('Teens', 'Teens'), ('Theater', 'Theater'), ('Time', 'Time'), ('Travel', 'Travel'), ('Trust', 'Trust'), ('Truth', 'Truth'), ('Vulnerability', 'Vulnerability'), ('War', 'War'), ('Wellness', 'Wellness'), ('Wisdom', 'Wisdom'), ('Wonder', 'Wonder'), ('Work', 'Work'), ('Writing', 'Writing'), ('Youth', 'Youth')], default='General', max_length=100), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /quotes/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoangsonww/Django-Quote-Application/572085c3a1d17e6c741bc1694711a92a899b68ab/quotes/migrations/__init__.py -------------------------------------------------------------------------------- /quotes/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | 4 | 5 | class Quote(models.Model): 6 | CATEGORY_CHOICES = [ 7 | ('Inspirational', 'Inspirational'), 8 | ('Motivational', 'Motivational'), 9 | ('General', 'General'), 10 | ('Love', 'Love'), 11 | ('Humor', 'Humor'), 12 | ('Philosophy', 'Philosophy'), 13 | ('Science', 'Science'), 14 | ('Technology', 'Technology'), 15 | ('Programming', 'Programming'), 16 | ('Business', 'Business'), 17 | ('Leadership', 'Leadership'), 18 | ('Success', 'Success'), 19 | ('Life', 'Life'), 20 | ('Friendship', 'Friendship'), 21 | ('Wisdom', 'Wisdom'), 22 | ('Education', 'Education'), 23 | ('Health', 'Health'), 24 | ('Fitness', 'Fitness'), 25 | ('Sports', 'Sports'), 26 | ('Music', 'Music'), 27 | ('Movies', 'Movies'), 28 | ('Art', 'Art'), 29 | ('Fashion', 'Fashion'), 30 | ('Food', 'Food'), 31 | ('Travel', 'Travel'), 32 | ('Nature', 'Nature'), 33 | ('Environment', 'Environment'), 34 | ('Politics', 'Politics'), 35 | ('Economics', 'Economics'), 36 | ('History', 'History'), 37 | ('Religion', 'Religion'), 38 | ('Spirituality', 'Spirituality'), 39 | ('Psychology', 'Psychology'), 40 | ('Self-Help', 'Self-Help'), 41 | ('Relationships', 'Relationships'), 42 | ('Parenting', 'Parenting'), 43 | ('Marriage', 'Marriage'), 44 | ('Family', 'Family'), 45 | ('Children', 'Children'), 46 | ('Teens', 'Teens'), 47 | ('Seniors', 'Seniors'), 48 | ('General', 'General'), 49 | ('Adventure', 'Adventure'), 50 | ('Animals', 'Animals'), 51 | ('Architecture', 'Architecture'), 52 | ('Books', 'Books'), 53 | ('Career', 'Career'), 54 | ('Comedy', 'Comedy'), 55 | ('Creativity', 'Creativity'), 56 | ('Culture', 'Culture'), 57 | ('Design', 'Design'), 58 | ('Dreams', 'Dreams'), 59 | ('Entertainment', 'Entertainment'), 60 | ('Finance', 'Finance'), 61 | ('Gardening', 'Gardening'), 62 | ('Happiness', 'Happiness'), 63 | ('Hobbies', 'Hobbies'), 64 | ('Home', 'Home'), 65 | ('Humanity', 'Humanity'), 66 | ('Innovation', 'Innovation'), 67 | ('Knowledge', 'Knowledge'), 68 | ('Language', 'Language'), 69 | ('Literature', 'Literature'), 70 | ('Mindfulness', 'Mindfulness'), 71 | ('Money', 'Money'), 72 | ('Motivation', 'Motivation'), 73 | ('Peace', 'Peace'), 74 | ('Poetry', 'Poetry'), 75 | ('Productivity', 'Productivity'), 76 | ('Science Fiction', 'Science Fiction'), 77 | ('Social Justice', 'Social Justice'), 78 | ('Society', 'Society'), 79 | ('Sustainability', 'Sustainability'), 80 | ('Time', 'Time'), 81 | ('Work', 'Work'), 82 | ('Activism', 'Activism'), 83 | ('Aging', 'Aging'), 84 | ('Astronomy', 'Astronomy'), 85 | ('Beauty', 'Beauty'), 86 | ('Belief', 'Belief'), 87 | ('Challenge', 'Challenge'), 88 | ('Change', 'Change'), 89 | ('Communication', 'Communication'), 90 | ('Community', 'Community'), 91 | ('Compassion', 'Compassion'), 92 | ('Confidence', 'Confidence'), 93 | ('Courage', 'Courage'), 94 | ('Curiosity', 'Curiosity'), 95 | ('Dance', 'Dance'), 96 | ('Diversity', 'Diversity'), 97 | ('Empathy', 'Empathy'), 98 | ('Empowerment', 'Empowerment'), 99 | ('Equality', 'Equality'), 100 | ('Ethics', 'Ethics'), 101 | ('Exploration', 'Exploration'), 102 | ('Faith', 'Faith'), 103 | ('Fantasy', 'Fantasy'), 104 | ('Fear', 'Fear'), 105 | ('Forgiveness', 'Forgiveness'), 106 | ('Freedom', 'Freedom'), 107 | ('Gender', 'Gender'), 108 | ('Gratitude', 'Gratitude'), 109 | ('Growth', 'Growth'), 110 | ('Healing', 'Healing'), 111 | ('Hope', 'Hope'), 112 | ('Human Rights', 'Human Rights'), 113 | ('Identity', 'Identity'), 114 | ('Imagination', 'Imagination'), 115 | ('Inclusion', 'Inclusion'), 116 | ('Independence', 'Independence'), 117 | ('Individuality', 'Individuality'), 118 | ('Inspiration', 'Inspiration'), 119 | ('Integrity', 'Integrity'), 120 | ('Justice', 'Justice'), 121 | ('Kindness', 'Kindness'), 122 | ('Learning', 'Learning'), 123 | ('LGBTQ+', 'LGBTQ+'), 124 | ('Loneliness', 'Loneliness'), 125 | ('Loss', 'Loss'), 126 | ('Magic', 'Magic'), 127 | ('Meaning', 'Meaning'), 128 | ('Memory', 'Memory'), 129 | ('Mental Health', 'Mental Health'), 130 | ('Minimalism', 'Minimalism'), 131 | ('Mythology', 'Mythology'), 132 | ('Opportunity', 'Opportunity'), 133 | ('Optimism', 'Optimism'), 134 | ('Patience', 'Patience'), 135 | ('Perspective', 'Perspective'), 136 | ('Philanthropy', 'Philanthropy'), 137 | ('Photography', 'Photography'), 138 | ('Resilience', 'Resilience'), 139 | ('Respect', 'Respect'), 140 | ('Responsibility', 'Responsibility'), 141 | ('Risk', 'Risk'), 142 | ('Self-Acceptance', 'Self-Acceptance'), 143 | ('Self-Care', 'Self-Care'), 144 | ('Self-Discovery', 'Self-Discovery'), 145 | ('Self-Esteem', 'Self-Esteem'), 146 | ('Self-Love', 'Self-Love'), 147 | ('Simplicity', 'Simplicity'), 148 | ('Solitude', 'Solitude'), 149 | ('Strength', 'Strength'), 150 | ('Theater', 'Theater'), 151 | ('Trust', 'Trust'), 152 | ('Truth', 'Truth'), 153 | ('Vulnerability', 'Vulnerability'), 154 | ('Wellness', 'Wellness'), 155 | ('Wonder', 'Wonder'), 156 | ('Youth', 'Youth'), 157 | ('Achievement', 'Achievement'), 158 | ('Activism', 'Activism'), 159 | ('Adventure', 'Adventure'), 160 | ('Adversity', 'Adversity'), 161 | ('Aging', 'Aging'), 162 | ('Ambition', 'Ambition'), 163 | ('Animals', 'Animals'), 164 | ('Anxiety', 'Anxiety'), 165 | ('Architecture', 'Architecture'), 166 | ('Art', 'Art'), 167 | ('Astronomy', 'Astronomy'), 168 | ('Authenticity', 'Authenticity'), 169 | ('Beauty', 'Beauty'), 170 | ('Belief', 'Belief'), 171 | ('Books', 'Books'), 172 | ('Bravery', 'Bravery'), 173 | ('Buddhism', 'Buddhism'), 174 | ('Business', 'Business'), 175 | ('Calm', 'Calm'), 176 | ('Career', 'Career'), 177 | ('Celebration', 'Celebration'), 178 | ('Challenge', 'Challenge'), 179 | ('Change', 'Change'), 180 | ('Childhood', 'Childhood'), 181 | ('Christianity', 'Christianity'), 182 | ('Cinema', 'Cinema'), 183 | ('Comedy', 'Comedy'), 184 | ('Communication', 'Communication'), 185 | ('Community', 'Community'), 186 | ('Compassion', 'Compassion'), 187 | ('Confidence', 'Confidence'), 188 | ('Consciousness', 'Consciousness'), 189 | ('Contentment', 'Contentment'), 190 | ('Courage', 'Courage'), 191 | ('Creativity', 'Creativity'), 192 | ('Criticism', 'Criticism'), 193 | ('Culture', 'Culture'), 194 | ('Curiosity', 'Curiosity'), 195 | ('Dance', 'Dance'), 196 | ('Death', 'Death'), 197 | ('Depression', 'Depression'), 198 | ('Design', 'Design'), 199 | ('Determination', 'Determination'), 200 | ('Diversity', 'Diversity'), 201 | ('Dreams', 'Dreams'), 202 | ('Economics', 'Economics'), 203 | ('Education', 'Education'), 204 | ('Empathy', 'Empathy'), 205 | ('Empowerment', 'Empowerment'), 206 | ('Energy', 'Energy'), 207 | ('Entertainment', 'Entertainment'), 208 | ('Environment', 'Environment'), 209 | ('Equality', 'Equality'), 210 | ('Ethics', 'Ethics'), 211 | ('Exploration', 'Exploration'), 212 | ('Failure', 'Failure'), 213 | ('Faith', 'Faith'), 214 | ('Family', 'Family'), 215 | ('Fantasy', 'Fantasy'), 216 | ('Fashion', 'Fashion'), 217 | ('Fear', 'Fear'), 218 | ('Feminism', 'Feminism'), 219 | ('Film', 'Film'), 220 | ('Finance', 'Finance'), 221 | ('Fitness', 'Fitness'), 222 | ('Food', 'Food'), 223 | ('Forgiveness', 'Forgiveness'), 224 | ('Freedom', 'Freedom'), 225 | ('Friendship', 'Friendship'), 226 | ('Gender', 'Gender'), 227 | ('Gratitude', 'Gratitude'), 228 | ('Grief', 'Grief'), 229 | ('Growth', 'Growth'), 230 | ('Happiness', 'Happiness'), 231 | ('Healing', 'Healing'), 232 | ('Health', 'Health'), 233 | ('Hinduism', 'Hinduism'), 234 | ('History', 'History'), 235 | ('Hobbies', 'Hobbies'), 236 | ('Home', 'Home'), 237 | ('Honesty', 'Honesty'), 238 | ('Hope', 'Hope'), 239 | ('Humanity', 'Humanity'), 240 | ('Humor', 'Humor'), 241 | ('Identity', 'Identity'), 242 | ('Imagination', 'Imagination'), 243 | ('Inclusion', 'Inclusion'), 244 | ('Independence', 'Independence'), 245 | ('Individuality', 'Individuality'), 246 | ('Innovation', 'Innovation'), 247 | ('Inspiration', 'Inspiration'), 248 | ('Integrity', 'Integrity'), 249 | ('Islam', 'Islam'), 250 | ('Judaism', 'Judaism'), 251 | ('Justice', 'Justice'), 252 | ('Kindness', 'Kindness'), 253 | ('Knowledge', 'Knowledge'), 254 | ('Language', 'Language'), 255 | ('Leadership', 'Leadership'), 256 | ('Learning', 'Learning'), 257 | ('Life', 'Life'), 258 | ('Literature', 'Literature'), 259 | ('Loneliness', 'Loneliness'), 260 | ('Loss', 'Loss'), 261 | ('Love', 'Love'), 262 | ('Magic', 'Magic'), 263 | ('Marriage', 'Marriage'), 264 | ('Meaning', 'Meaning'), 265 | ('Memory', 'Memory'), 266 | ('Minimalism', 'Minimalism'), 267 | ('Mindfulness', 'Mindfulness'), 268 | ('Money', 'Money'), 269 | ('Motivation', 'Motivation'), 270 | ('Music', 'Music'), 271 | ('Mystery', 'Mystery'), 272 | ('Mythology', 'Mythology'), 273 | ('Nature', 'Nature'), 274 | ('Opportunity', 'Opportunity'), 275 | ('Optimism', 'Optimism'), 276 | ('Parenting', 'Parenting'), 277 | ('Patience', 'Patience'), 278 | ('Peace', 'Peace'), 279 | ('Perseverance', 'Perseverance'), 280 | ('Perspective', 'Perspective'), 281 | ('Philanthropy', 'Philanthropy'), 282 | ('Philosophy', 'Philosophy'), 283 | ('Photography', 'Photography'), 284 | ('Poetry', 'Poetry'), 285 | ('Politics', 'Politics'), 286 | ('Positivity', 'Positivity'), 287 | ('Poverty', 'Poverty'), 288 | ('Power', 'Power'), 289 | ('Productivity', 'Productivity'), 290 | ('Programming', 'Programming'), 291 | ('Psychology', 'Psychology'), 292 | ('Racism', 'Racism'), 293 | ('Relationships', 'Relationships'), 294 | ('Religion', 'Religion'), 295 | ('Resilience', 'Resilience'), 296 | ('Respect', 'Respect'), 297 | ('Responsibility', 'Responsibility'), 298 | ('Risk', 'Risk'), 299 | ('Science', 'Science'), 300 | ('Self-Acceptance', 'Self-Acceptance'), 301 | ('Self-Care', 'Self-Care'), 302 | ('Self-Discovery', 'Self-Discovery'), 303 | ('Self-Esteem', 'Self-Esteem'), 304 | ('Self-Love', 'Self-Love'), 305 | ('Sexuality', 'Sexuality'), 306 | ('Simplicity', 'Simplicity'), 307 | ('Society', 'Society'), 308 | ('Solitude', 'Solitude'), 309 | ('Spirituality', 'Spirituality'), 310 | ('Sports', 'Sports'), 311 | ('Strength', 'Strength'), 312 | ('Success', 'Success'), 313 | ('Sustainability', 'Sustainability'), 314 | ('Technology', 'Technology'), 315 | ('Teens', 'Teens'), 316 | ('Theater', 'Theater'), 317 | ('Time', 'Time'), 318 | ('Travel', 'Travel'), 319 | ('Trust', 'Trust'), 320 | ('Truth', 'Truth'), 321 | ('Vulnerability', 'Vulnerability'), 322 | ('War', 'War'), 323 | ('Wellness', 'Wellness'), 324 | ('Wisdom', 'Wisdom'), 325 | ('Wonder', 'Wonder'), 326 | ('Work', 'Work'), 327 | ('Writing', 'Writing'), 328 | ('Youth', 'Youth') 329 | ] 330 | 331 | text = models.TextField() 332 | author = models.CharField(max_length=100) 333 | category = models.CharField(max_length=100, choices=CATEGORY_CHOICES, default='General') 334 | 335 | def __str__(self): 336 | return self.text 337 | 338 | 339 | class Like(models.Model): 340 | user = models.ForeignKey(User, on_delete=models.CASCADE) 341 | quote = models.ForeignKey(Quote, on_delete=models.CASCADE) 342 | 343 | 344 | class Comment(models.Model): 345 | user = models.ForeignKey(User, on_delete=models.CASCADE) 346 | quote = models.ForeignKey(Quote, on_delete=models.CASCADE) 347 | text = models.TextField() 348 | created_at = models.DateTimeField(auto_now_add=True) 349 | -------------------------------------------------------------------------------- /quotes/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from .models import Quote, Comment 3 | 4 | 5 | class QuoteSerializer(serializers.ModelSerializer): 6 | class Meta: 7 | model = Quote 8 | fields = ['id', 'text', 'author', 'category', 'like_set'] 9 | 10 | 11 | class CommentSerializer(serializers.ModelSerializer): 12 | user = serializers.CharField(source='user.username', read_only=True) # Get username 13 | 14 | class Meta: 15 | model = Comment 16 | fields = ['id', 'user', 'text', 'created_at'] 17 | -------------------------------------------------------------------------------- /quotes/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoangsonww/Django-Quote-Application/572085c3a1d17e6c741bc1694711a92a899b68ab/quotes/static/images/favicon.ico -------------------------------------------------------------------------------- /quotes/templates/quotes/category.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ category_name }} Quotes 6 | 7 | 100 | 101 | 102 |
103 |

{{ category_name }} Quotes

104 |
105 |
106 | {% if quotes %} 107 |
    108 | {% for quote in quotes %} 109 |
  • 110 | "{{ quote.text }}" - {{ quote.author }} 111 | 120 |

    Likes: {{ quote.like_set.count }}

    121 |

    Comments

    122 |
      123 | {% for comment in quote.comment_set.all %} 124 |
    • {{ comment.user.username }} - {{ comment.text }}
    • 125 | {% endfor %} 126 |
    127 |
    128 | {% csrf_token %} 129 | 130 | 131 |
    132 |
  • 133 | {% endfor %} 134 |
135 | {% else %} 136 |

No quotes found in this category.

137 | {% endif %} 138 |
139 | 140 | 141 | 142 |
143 |

© 2024 The Django Quote App - By Son Nguyen

144 |
145 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /quotes/templates/quotes/comment_item.html: -------------------------------------------------------------------------------- 1 |
    2 |
  • {{ comment.user.username }} - {{ comment.text }}
  • 3 |
4 | -------------------------------------------------------------------------------- /quotes/templates/quotes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quote of the Day 5 | 6 | 163 | 164 | 165 |
166 |

Quote of the Day

167 |
168 |
169 | {% if quote %} 170 |

{{ quote.text }}

171 |

- By {{ quote.author }}

172 |

Category: {{ quote.get_category_display }}

173 | {% if user.is_authenticated %} 174 | 183 |

Likes: {{ quote.like_set.count }}

184 |

Comments

185 |
    186 | {% for comment in quote.comment_set.all %} 187 |
  • {{ comment.user.username }} - {{ comment.text }}
  • 188 | {% endfor %} 189 |
190 |
191 | {% csrf_token %} 192 | 193 | 194 | 195 |
196 | {% else %} 197 |

Please log in to like and comment.

198 | {% endif %} 199 | {% else %} 200 |

No quotes available.

201 | {% endif %} 202 |
Note: Only logged-in administrative users can create quotes. Only authenticated users can like and comment on quotes.
203 |
204 | 211 |
212 |

View quotes by category:

213 |
214 | 222 | 223 |
224 |
225 | 277 | 278 |
279 |

© 2024 The Django Quote App - By Son Nguyen

280 |
281 | -------------------------------------------------------------------------------- /quotes/templates/quotes/quote_detail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | "{{ quote.text }}" - Quote Detail 6 | 7 | 161 | 162 | 163 |
164 |

Quote Detail

165 |
166 |
167 | {% if quote %} 168 |

{{ quote.text }}

169 |

- By {{ quote.author }}

170 |

Category: {{ quote.get_category_display }}

171 | {% if user.is_authenticated %} 172 | 181 |

Likes: {{ quote.like_set.count }}

182 |

Comments

183 |
    184 | {% for comment in quote.comment_set.all %} 185 | {% include 'quotes/comment_item.html' %} 186 | {% endfor %} 187 |
188 | 189 |
190 | {% csrf_token %} 191 | 192 | 193 |
194 | {% else %} 195 |

Please log in to like and comment.

196 | {% endif %} 197 | {% else %} 198 |

Quote not found.

199 | {% endif %} 200 |
Note: Only logged-in administrative users can create quotes. Only authenticated users can like and comment on quotes.
201 |
202 | 209 |
210 |

View quotes by category:

211 |
212 | 220 | 221 |
222 |
223 | 224 | 225 | 226 |
227 |

© 2024 The Django Quote App - By Son Nguyen

228 |
229 | 278 | 279 | 280 | -------------------------------------------------------------------------------- /quotes/templates/quotes/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Search Results 6 | 7 | 85 | 86 | 87 | 88 |

Search Results

89 | {% if query %} 90 |

{{ quotes.count }} results found for "{{ query }}".

91 | 100 | {% else %} 101 |

Please enter a search query.

102 | {% endif %} 103 | 104 | 105 | 106 |
107 |

© 2024 The Django Quote App - By Son Nguyen

108 |
109 | 110 | -------------------------------------------------------------------------------- /quotes/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase, Client 2 | from django.urls import reverse 3 | from django.contrib.auth.models import User 4 | 5 | from .models import Quote, Like, Comment 6 | 7 | 8 | class QuoteTestCase(TestCase): 9 | def setUp(self): 10 | self.user = User.objects.create_user(username='testuser', password='12345') 11 | self.quote = Quote.objects.create(text='Test Quote', author='Test Author', category='General') 12 | 13 | def test_quote_creation(self): 14 | self.assertEqual(self.quote.text, 'Test Quote') 15 | self.assertEqual(self.quote.author, 'Test Author') 16 | self.assertEqual(self.quote.category, 'General') 17 | 18 | 19 | class ViewsTestCase(TestCase): 20 | def setUp(self): 21 | self.client = Client() 22 | self.index_url = reverse('index') 23 | self.search_url = reverse('search') 24 | self.category_url = reverse('category_view', args=['General']) 25 | 26 | def test_index_view(self): 27 | response = self.client.get(self.index_url) 28 | self.assertEqual(response.status_code, 200) 29 | 30 | def test_search_view(self): 31 | response = self.client.get(self.search_url) 32 | self.assertEqual(response.status_code, 200) 33 | 34 | def test_category_view(self): 35 | response = self.client.get(self.category_url) 36 | self.assertEqual(response.status_code, 200) 37 | 38 | 39 | class SearchTestCase(TestCase): 40 | def setUp(self): 41 | self.client = Client() 42 | self.search_url = reverse('search') 43 | Quote.objects.create(text='Test Quote', author='Test Author', category='General') 44 | 45 | def test_search_quote(self): 46 | response = self.client.get(self.search_url, {'query': 'Test Quote'}) 47 | self.assertContains(response, 'Test Quote') 48 | -------------------------------------------------------------------------------- /quotes/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | from django.contrib import admin 3 | from . import views 4 | from rest_framework.routers import DefaultRouter 5 | 6 | admin.site.site_header = "Quotes Application Administration" 7 | 8 | router = DefaultRouter() 9 | router.register(r'quotes', views.QuoteViewSet) 10 | router.register(r'quotes/(?P\d+)/comments', views.CommentViewSet, basename='quote-comments') 11 | 12 | urlpatterns = [ 13 | path('', views.index, name='index'), 14 | path('search/', views.search, name='search'), 15 | path('/', views.quote_detail, name='quote_detail'), 16 | path('/', views.category_view, name='category'), 17 | path('like//', views.like_quote, name='like_quote'), 18 | path('comment//', views.add_comment, name='add_comment'), 19 | path('api/', include(router.urls)), 20 | ] 21 | -------------------------------------------------------------------------------- /quotes/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from django.shortcuts import get_object_or_404 3 | from django.http import JsonResponse 4 | from django.template.loader import render_to_string 5 | from django.views.decorators.http import require_POST 6 | from .models import Quote, Like, Comment 7 | from rest_framework import viewsets 8 | from rest_framework.permissions import IsAuthenticatedOrReadOnly 9 | from .serializers import QuoteSerializer, CommentSerializer 10 | import random 11 | 12 | 13 | def index(request): 14 | quotes = Quote.objects.all() 15 | random_quote = random.choice(quotes) if quotes else None 16 | return render(request, 'quotes/index.html', {'quote': random_quote}) 17 | 18 | 19 | def search(request): 20 | query = request.GET.get('query', '') 21 | quotes = Quote.objects.filter(text__icontains=query) if query else Quote.objects.none() 22 | return render(request, 'quotes/search.html', {'quotes': quotes, 'query': query}) 23 | 24 | 25 | def category_view(request, category_name): 26 | quotes = Quote.objects.filter(category=category_name) 27 | context = { 28 | 'quotes': quotes, 29 | 'category_name': category_name.capitalize() 30 | } 31 | return render(request, 'quotes/category.html', context) 32 | 33 | 34 | def like_quote(request, quote_id): 35 | quote = get_object_or_404(Quote, pk=quote_id) 36 | user = request.user 37 | 38 | if user.is_authenticated: 39 | like, created = Like.objects.get_or_create(user=user, quote=quote) 40 | if not created: 41 | like.delete() 42 | liked = False # Indicate that the quote was unliked 43 | else: 44 | liked = True # Indicate that the quote was liked 45 | else: 46 | liked = False # For unauthenticated users 47 | 48 | return JsonResponse({'liked': liked, 'like_count': quote.like_set.count()}) 49 | 50 | 51 | @require_POST 52 | def add_comment(request, quote_id): 53 | quote = get_object_or_404(Quote, pk=quote_id) 54 | user = request.user 55 | 56 | comment_text = request.POST.get('comment_text') 57 | if comment_text: 58 | comment = Comment.objects.create(user=user, quote=quote, text=comment_text) 59 | 60 | # Render the new comment as HTML 61 | html = render_to_string('quotes/comment_item.html', {'comment': comment}) 62 | 63 | return JsonResponse({'html': html}) 64 | else: 65 | return JsonResponse({'error': 'Comment text is required'}, status=400) 66 | 67 | 68 | def quote_detail(request, quote_id): 69 | quote = get_object_or_404(Quote, pk=quote_id) 70 | return render(request, 'quotes/quote_detail.html', {'quote': quote}) 71 | 72 | 73 | class QuoteViewSet(viewsets.ModelViewSet): 74 | queryset = Quote.objects.all() 75 | serializer_class = QuoteSerializer 76 | permission_classes = [ 77 | IsAuthenticatedOrReadOnly] 78 | filterset_fields = ['category'] 79 | 80 | 81 | class CommentViewSet(viewsets.ModelViewSet): 82 | queryset = Comment.objects.all() 83 | serializer_class = CommentSerializer 84 | permission_classes = [IsAuthenticatedOrReadOnly] 85 | 86 | def get_queryset(self): 87 | quote_id = self.kwargs.get('quote_id') 88 | return Comment.objects.filter(quote_id=quote_id) 89 | -------------------------------------------------------------------------------- /readme-images/api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoangsonww/Django-Quote-Application/572085c3a1d17e6c741bc1694711a92a899b68ab/readme-images/api.png -------------------------------------------------------------------------------- /readme-images/category.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoangsonww/Django-Quote-Application/572085c3a1d17e6c741bc1694711a92a899b68ab/readme-images/category.png -------------------------------------------------------------------------------- /readme-images/details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoangsonww/Django-Quote-Application/572085c3a1d17e6c741bc1694711a92a899b68ab/readme-images/details.png -------------------------------------------------------------------------------- /readme-images/homepage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoangsonww/Django-Quote-Application/572085c3a1d17e6c741bc1694711a92a899b68ab/readme-images/homepage.png -------------------------------------------------------------------------------- /readme-images/results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoangsonww/Django-Quote-Application/572085c3a1d17e6c741bc1694711a92a899b68ab/readme-images/results.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | altair 2 | amqp 3 | APScheduler 4 | asgiref 5 | astroid 6 | attrs 7 | autopep8 8 | bcrypt 9 | beautifulsoup4 10 | bidict 11 | billiard 12 | blinker 13 | cachelib 14 | cachetools 15 | celery 16 | certifi 17 | cffi 18 | charset-normalizer 19 | click 20 | click-didyoumean 21 | click-plugins 22 | click-repl 23 | coloredlogs 24 | decorator 25 | django-debug-toolbar 26 | django-redis 27 | django-rest-framework-mongoengine 28 | Django==3.2 29 | djangorestframework==3.12.4 30 | djongo 31 | dnspython 32 | eventlet 33 | filelock 34 | Flask 35 | Flask-Cors 36 | Flask-Session 37 | Flask-SocketIO 38 | flatbuffers 39 | fsspec 40 | gitdb 41 | GitPython 42 | greenlet 43 | gunicorn 44 | h11 45 | huggingface-hub 46 | humanfriendly 47 | idna 48 | imageio 49 | imageio-ffmpeg 50 | isort 51 | itsdangerous 52 | Jinja2 53 | joblib 54 | jsonschema 55 | jsonschema-specifications 56 | kombu 57 | lazy-object-proxy 58 | lazy_loader 59 | llvmlite 60 | markdown-it-py 61 | MarkupSafe 62 | mccabe 63 | mdurl 64 | mongoengine 65 | moviepy 66 | mpmath 67 | mysqlclient 68 | networkx 69 | nltk 70 | numba 71 | numpy 72 | onnxruntime 73 | opencv-python-headless 74 | packaging 75 | pandas 76 | pillow 77 | platformdirs 78 | pooch 79 | proglog 80 | prompt_toolkit 81 | protobuf 82 | psycopg2 83 | psycopg2-binary 84 | pyarrow 85 | pycodestyle 86 | pycparser 87 | pydeck 88 | pygame 89 | Pygments 90 | pylint 91 | PyMatting 92 | pymongo 93 | python-dateutil 94 | python-decouple 95 | python-dotenv 96 | python-engineio 97 | python-socketio 98 | pytz 99 | PyYAML 100 | redis 101 | referencing 102 | regex 103 | rembg 104 | requests 105 | rich 106 | rpds-py 107 | safetensors 108 | scikit-image 109 | scikit-learn 110 | scipy 111 | setuptools 112 | simple-websocket 113 | six 114 | smmap 115 | soupsieve 116 | sqlparse 117 | streamlit 118 | sympy 119 | tenacity 120 | threadpoolctl 121 | tifffile 122 | tokenizers 123 | toml 124 | toolz 125 | torch 126 | torchvision 127 | tornado 128 | tqdm 129 | transformers 130 | typing_extensions 131 | tzdata 132 | tzlocal 133 | urllib3 134 | vine 135 | wcwidth 136 | Werkzeug 137 | wrapt 138 | wsproto 139 | zope.event 140 | zope.interface 141 | --------------------------------------------------------------------------------