Commit fad0df08 authored by Volkov Gherman's avatar Volkov Gherman

завершил Курсовую работу с новостным проектом

parent bde08a12
asgiref==3.4.1 asgiref==3.4.1
Django==3.2.9 Django==3.2.9
django-crispy-forms==1.13.0 django-crispy-forms==1.13.0
Markdown==3.3.4
pytz==2021.3 pytz==2021.3
sqlparse==0.4.2 sqlparse==0.4.2
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="db" uuid="fb1dd082-ca0e-419a-affb-8cf1144abbd9">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:C:\Users\Герман\Desktop\python_dev\django\62\Сoursework\News\source\db.sqlite3</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
<libraries>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.34.0/sqlite-jdbc-3.34.0.jar</url>
</library>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.34.0/sqlite-jdbc-3.34.0.jar</url>
</library>
</libraries>
</data-source>
</component>
</project>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="2">
<item index="0" class="java.lang.String" itemvalue="django-crispy-forms" />
<item index="1" class="java.lang.String" itemvalue="Markdown" />
</list>
</value>
</option>
</inspection_tool>
</profile>
</component>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="3.8 @ Ubuntu (12)" project-jdk-type="Python SDK" />
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/source.iml" filepath="$PROJECT_DIR$/.idea/source.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="FacetManager">
<facet type="django" name="Django">
<configuration>
<option name="rootFolder" value="$MODULE_DIR$" />
<option name="settingsModule" value="news/settings.py" />
<option name="manageScript" value="$MODULE_DIR$/manage.py" />
<option name="environment" value="&lt;map/&gt;" />
<option name="doNotUseTestRunner" value="false" />
<option name="trackFilePattern" value="migrations" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="3.8 @ Ubuntu (12)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PyDocumentationSettings">
<option name="format" value="PLAIN" />
<option name="myDocStringFormat" value="Plain" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Django" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/webapp/templates" />
<option value="$MODULE_DIR$/accounts/templates" />
</list>
</option>
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>
\ No newline at end of file
Проект с кодовым названием NEWS, нужно склонировать, установить зависимости из файла requirements.txt, загрузить фикстуры
\ No newline at end of file
from django.contrib.auth.forms import UserCreationForm
class RegistrationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
fields = ["username", "password1", "password2"]
{% extends 'base.html' %}
{% load crispy_forms_filters %}
{% block content %}
<form action="{% url 'accounts:login' %}" method="post">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-outline-dark log_btn" type="submit">Login</button>
</form>
{% endblock %}
\ No newline at end of file
{% extends 'base.html' %}
{% load crispy_forms_filters %}
{% block content %}
<div class="log">
<form action="{% url 'accounts:password_change_done' request.user.pk %}" method="post">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-outline-dark log_btn" type="submit">Login</button>
</form>
</div>
{% endblock %}
\ No newline at end of file
{% extends 'base.html' %}
{% load crispy_forms_filters %}
{% block content %}
<div class="log">
<form action="{% url 'accounts:register' %}" method="post">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-outline-dark log_btn" type="submit">Sign up</button>
</form>
</div>
{% endblock %}
\ No newline at end of file
from django.contrib.auth.views import LoginView, LogoutView, PasswordChangeView
from django.urls import path
from accounts.views import RegisterView
app_name = "accounts"
urlpatterns = [
path("account/login/", LoginView.as_view(), name="login"),
path("account/logout/", LogoutView.as_view(), name="logout"),
path("account/registration/", RegisterView.as_view(), name="register"),
path("account/<int:pk>/change_password", PasswordChangeView.as_view(), name="password_change_done"),
]
from django.shortcuts import render from django.contrib.auth import get_user_model, login
from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.views.generic import CreateView
from accounts.forms import RegistrationForm
class RegisterView(CreateView):
model = get_user_model()
template_name = 'registration/register.html'
form_class = RegistrationForm
success_url = reverse_lazy('webapp:index')
def form_valid(self, form):
user = form.save()
login(self.request, user)
return redirect("webapp:index")
# Create your views here.
No preview for this file type
This diff is collapsed.
...@@ -31,7 +31,6 @@ ALLOWED_HOSTS = [] ...@@ -31,7 +31,6 @@ ALLOWED_HOSTS = []
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
...@@ -39,6 +38,7 @@ INSTALLED_APPS = [ ...@@ -39,6 +38,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'webapp', 'webapp',
'accounts', 'accounts',
'django.contrib.admin',
'crispy_forms' 'crispy_forms'
] ]
......
...@@ -14,8 +14,10 @@ Including another URLconf ...@@ -14,8 +14,10 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import path from django.urls import path, include
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('', include('webapp.urls')),
path('', include('accounts.urls')),
] ]
from django.contrib import admin from django.contrib import admin
# Register your models here. from webapp.models import Post, Comment
admin.site.register(Post)
admin.site.register(Comment)
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from webapp.models import Post
class PostForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.add_input(Submit('submit', 'Submit'))
class Meta:
model = Post
exclude = ['create']
widgets = {
'is_active': forms.CheckboxInput()
}
class SearchForm(forms.Form):
q = forms.CharField(max_length=30, required=False, label="Search")
# Generated by Django 3.2.9 on 2021-11-03 10:05
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Post',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200, verbose_name='Post title')),
('pic', models.URLField(blank=True, null=True, verbose_name='Post pics')),
('text', models.TextField(max_length=2000, verbose_name='Post text')),
('create', models.DateField(auto_now_add=True, verbose_name='Post create time')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
],
),
migrations.CreateModel(
name='Comment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.TextField(max_length=500, verbose_name='Comment text')),
('create', models.DateField(auto_now_add=True, verbose_name='Comment create time')),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to=settings.AUTH_USER_MODEL, verbose_name='Comment author')),
('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='webapp.post', verbose_name='Post')),
],
),
]
from django.contrib.auth import get_user_model
from django.db import models from django.db import models
class Post(models.Model): class Post(models.Model):
pass title = models.CharField(max_length=200, blank=False, null=False, verbose_name='Post title')
pic = models.URLField(max_length=200, blank=True, null=True, verbose_name='Post pics')
text = models.TextField(max_length=2000, blank=False, null=False, verbose_name='Post text')
create = models.DateField(auto_now_add=True, verbose_name='Post create time')
is_active = models.BooleanField(default=True, verbose_name='Is active')
def __str__(self):
return self.title
class Comment(models.Model): class Comment(models.Model):
pass author = models.ForeignKey(get_user_model(), verbose_name='Comment author', related_name='comments', on_delete=models.CASCADE)
post = models.ForeignKey('webapp.Post', verbose_name='Post', related_name='comments', on_delete=models.CASCADE)
text = models.TextField(max_length=500, blank=False, null=False, verbose_name='Comment text')
create = models.DateField(auto_now_add=True, verbose_name='Comment create time')
def __str__(self):
return self.author
body {
margin: 0;
}
footer {
position: absolute;
bottom: 0;
right: 0;
left: 0;
}
/*BASE*/
.nav_right_container {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.icon {
font-size: 1.5rem;
}
.reg_cont {
margin-right: 10px;
}
/*INDEX*/
/*POST DETAIL*/
.post_detail_container {
margin: 30px auto;
}
.post_d_body {
background: #f0f0f0;
}
.post_d_title {
text-align: center;
}
.post_d_text {
padding: 10px 30px;
}
.post_d_buttons {
display: flex;
justify-content: space-between;
padding: 0 20px;
}
/*COMMENTS*/
.comment_input {
height: 50px;
width: 40rem;
margin-right: 5px
}
.comment_container {
width: 500px;
margin: 10px auto;
border-radius: 3px;
border: 1px darkgray solid;
}
.com_header {
background: #F8F9FA;
text-align: center;
padding: 5px;
}
.com_text {
background: #f0f0f0;
padding: 5px;
}
/*ACCOUNTS*/
.log {
align-items: center;
}
.log_btn {
margin:10px auto;
}
\ No newline at end of file
{% load static %}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.6.0/font/bootstrap-icons.css">
<link rel="stylesheet" href="{% static "css/main.css" %}">
<title>Document</title>
</head>
<body>
{% block header %}
<nav class="navbar navbar-light bg-light border border-bottom-4">
<div class="d-flex justify-content-between w-100 p-2 align-items-center">
<div>
<a class="navbar-brand" title="main page" href="{% url "webapp:index" %}">
<h1 class="logo">News</h1>
</a>
</div>
<div class="nav_right_container">
{% if user.is_authenticated %}
<div style="margin-right: 10px;" class="link_anon d-flex justify-content-between align-items-center">
<form action="{% url "accounts:logout" %}" method="post">
{% csrf_token %}
<button class="btn text-dark btn-link" title="Logout"><i class="bi bi-door-closed icon"></i></button>
</form>
{% if requert.user.is_superuser %}
<a style="margin-right: 10px;" href="{% url "webapp:post_create" %}" class="text-dark" title="Add post"><i class="bi bi-file-plus icon"></i></a>
{% endif %}
<a style="margin-right: 10px;" href="{% url "accounts:password_change_done" request.user.pk %}" class="text-dark" title="Change password"><i class="bi bi-pencil icon"></i></a>
</div>
{% else %}
<div class="link_anon d-flex justify-content-between reg_cont">
<div class="reg_cont">
<a href="{% url "accounts:register" %}" class="text-dark" title="Registration"><i class="bi bi-person-plus icon"></i></a>
</div>
<a href="{% url "accounts:login" %}" class="text-dark" title="Login"><i class="bi bi-door-open icon"></i></a>
</div>
{% endif %}
<div>
<form class="d-flex" action="{% url 'webapp:search' %}" method="get">
<input class="form-control me-2" name="q" type="search" placeholder="Search" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
{% endblock %}
{% block profile %}
{% endblock %}
<div class="container">
<div class="block">
{% block title %}{% endblock %}
{% block content %}
{% endblock %}
</div>
</div>
{% block footer %}
<footer style="background: #F8F9FA" class="p-4 d-flex justify-content-around border border-top-2">
<p>© 2021 NEWS</p>
<p class="fst-italic">
Designed by GoGa Gogich. All rights reserved.
</p>
</footer>
{% endblock %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity=
"sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>
</html>
\ No newline at end of file
<form action="{% url 'webapp:comment_delete' comment.pk%}" method="post">
{% csrf_token %}
<button type="submit" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
<span title="Delete" aria-hidden="true">&times;</span>
</button>
</form>
\ No newline at end of file
<form action="{% url 'webapp:comment_create' post.pk %}" method="post">
{% csrf_token %}
<div class="d-flex justify-content-between align-items-between">
<input class="d-block input_comment comment_input" name="text" type="text" placeholder="Write comment">
<button class="btn btn-outline-dark" type="submit">Comment</button>
</div>
</form>
\ No newline at end of file
{% extends "base.html" %}
{% block title %}{% endblock %}
{% block content %}
{% for post in posts %}
<div class="card post_detail_container" style="width: 50rem;">
{% if post.pic %}
<img src="{{ post.pic }}" class="card-img-top" alt="pic">
{% endif %}
<div class="card-body">
<h5 class="card-title post_d_title">{{ post.title }}</h5>
<p class="card-text post_d_text">{{ post.text }}</p>
<hr>
<p class="card-text"><i>{{ post.create }}</i></p>
<a href="{% url 'webapp:post_detail' post.pk %}" class="btn btn-outline-dark">In detail..</a>
</div>
</div>
{% endfor %}
{% endblock %}
\ No newline at end of file
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block title %}{% endblock %}
{% block content %}
<div class="post_create_container">
<h2>Add new Post</h2>
{% crispy form form.helper %}
<a class="btn btn-outline-dark" href="{% url 'webapp:index' %}">Back to main</a>
</div>
{% endblock %}
<form action="{% url 'webapp:post_delete' post.pk %}" method="post">
{% csrf_token %}
<button type="submit" class="btn btn-outline-danger" onclick="return confirm('Are you sure?')">Delete</button>
</form>
\ No newline at end of file
{% extends "base.html" %}
{% block title %}{% endblock %}
{% block content %}
<div class="card post_detail_container" style="width: 50rem;">
{% if post.pic %}
<img src="{{ post.pic }}" class="card-img-top" alt="pic">
{% endif %}
<div class="card-body post_d_body">
<h5 class="card-title post_d_title">{{ post.title }}</h5>
<hr>
<p class="card-text post_d_text">{{ post.text }}</p>
<hr>
<p class="card-text"><i>{{ post.create }}</i></p>
{% if request.user.is_superuser %}
<div class="post_d_buttons">
<a href="{% url 'webapp:post_update' post.pk %}" class="btn btn-outline-dark">Update</a>
{% include 'post/post_delete.html' %}
</div>
{% endif %}
<hr>
{% include 'comment/comment_form.html' %}
</div>
</div>
{% if comments %}
{% for comment in comments %}
<div class="toast comment_container" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header com_header">
{# <img src="..." class="rounded mr-2" alt="...">#}
{% if request.user.is_superuser %}
{% include 'comment/comment_delete.html' %}
{% endif %}
<strong class="mr-auto">{{ comment.author }}</strong>
<small class="text-muted">{{ comment.create }}</small>
</div>
<div class="toast-body com_text">
{{ comment.text }}
</div>
</div>
{% endfor %}
{% endif %}
{% endblock %}
\ No newline at end of file
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block title %}{% endblock %}
{% block content %}
<div class="book_create_container">
<h2>Update Post {{ post.title }}</h2>
{% crispy form form.helper %}
<a class="btn btn-outline-dark" href="{% url 'webapp:index' %}">Back to main</a>
</div>
{% endblock %}
{% extends "base.html" %}
{% block title %}{% endblock %}
{% block content %}
<div class="card" style="width: 18rem; margin: 50px auto;">
<div class="card-header">
<h5>Search results</h5>
</div>
<ul class="list-group list-group-flush">
{% for post in posts %}
<li class="list-group-item"><a style="color: black" href="{% url 'webapp:post_detail' post.pk %}">{{ post.title }}</a></li>
{% empty %}
<li class="list-group-item">No results</li>
{% endfor %}
</ul>
</div>
{% endblock %}
\ No newline at end of file
from django.urls import path
from webapp import views as webapp_views
app_name = "webapp"
urlpatterns = [
path("", webapp_views.PostListView.as_view(), name="index"),
path("post/new/", webapp_views.PostCreateView.as_view(), name="post_create"),
path("post/<int:pk>/", webapp_views.PostDetailView.as_view(), name="post_detail"),
path("post/<int:pk>/edit/", webapp_views.PostUpdateView.as_view(), name="post_update"),
path("post/<int:pk>/delete/", webapp_views.PostDeleteView.as_view(), name="post_delete"),
path("post/<int:p_pk>/comment/new/", webapp_views.CommentCreateView.as_view(), name="comment_create"),
path("comment/<int:pk>/delete/", webapp_views.CommentDeleteView.as_view(), name="comment_delete"),
path("search/", webapp_views.SearchView.as_view(), name="search")
]
from django.shortcuts import render from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Q
from django.shortcuts import get_object_or_404
from django.urls import reverse, reverse_lazy
from django.utils.http import urlencode
from django.views.generic import CreateView, ListView, DetailView, UpdateView, DeleteView
from webapp.forms import PostForm, SearchForm
from webapp.models import Post, Comment
class PostListView(ListView):
model = Post
template_name = 'index.html'
context_object_name = 'posts'
ordering = ['-create']
paginate_by = 10
def get_queryset(self):
queryset = super().get_queryset().filter(is_active=True)
return queryset
class PostDetailView(DetailView):
model = Post
template_name = 'post/post_detail.html'
context_object_name = 'post'
pk_url_kwarg = 'pk'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
comments = self.get_object().comments.order_by('-create')
context['comments'] = comments
return context
class PostCreateView(PermissionRequiredMixin, CreateView):
model = Post
template_name = 'post/post_create.html'
form_class = PostForm
permission_required = 'webapp.add_post'
def get_success_url(self):
return reverse('webapp:post_detail', kwargs={"pk": self.object.pk})
class PostUpdateView(PermissionRequiredMixin, UpdateView):
model = Post
context_object_name = 'post'
template_name = 'post/post_update.html'
form_class = PostForm
permission_required = 'webapp.change_post'
def get_success_url(self):
return reverse('webapp:post_detail', kwargs={"pk": self.object.pk})
class PostDeleteView(PermissionRequiredMixin, DeleteView):
model = Post
template_name = 'post/post_delete.html'
pk_url_kwarg = 'pk'
success_url = reverse_lazy('webapp:index')
permission_required = 'webapp.delete_post'
class CommentCreateView(CreateView):
model = Comment
template_name = 'comment/comment_form.html'
fields = ['text']
def form_valid(self, form):
form.instance.post = self.get_object()
form.instance.author = self.request.user
return super().form_valid(form)
def get_object(self, queryset=None):
pk = self.kwargs.get("p_pk")
return get_object_or_404(Post, pk=pk)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['post'] = self.get_object()
return self.get_context_data(context)
def get_success_url(self):
return reverse('webapp:post_detail', kwargs={"pk": self.get_object().pk})
class CommentDeleteView(PermissionRequiredMixin, DeleteView):
model = Comment
template_name = 'comment/comment_delete.html'
pk_url_kwarg = 'pk'
permission_required = 'webapp.delete_comment'
def has_permission(self):
return super().has_permission() or self.object == self.request.user
def get_success_url(self):
return reverse('webapp:post_detail', kwargs={"pk": self.object.post.pk})
class SearchView(ListView):
model = Post
template_name = 'post/search_list.html'
context_object_name = 'posts'
def get(self, request, *args, **kwargs):
self.form = self.get_search_form()
self.search_value = self.get_search_value()
return super().get(request, *args, **kwargs)
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(object_list=object_list, **kwargs)
context['search_form'] = self.form
if self.search_value:
context['query'] = urlencode({'q': self.search_value})
return context
def get_queryset(self):
query_set = super().get_queryset()
if self.search_value:
query = Q(title__icontains=self.search_value) | Q(text__icontains=self.search_value)
query_set = query_set.filter(query)
return query_set
def get_search_form(self):
return SearchForm(self.request.GET)
def get_search_value(self):
if self.form.is_valid():
return self.form.cleaned_data['q']
# Create your views here.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment