Warning: count(): Parameter must be an array or an object that implements Countable in /home/xs638785/agile-software.site/public_html/wp-content/plugins/rich-table-of-content/functions.php on line 490
教材一覧
class Work(models.Model):
title = models.CharField('タイトル', max_length=100)
image = models.ImageField(upload_to='images', verbose_name='イメージ画像')
thumbnail = models.ImageField(upload_to='images', verbose_name='サムネイル', null=True, blank=True)
skill = models.CharField('スキル', max_length=100)
url = models.CharField('URL', max_length=100, null=True, blank=True)
created = models.DateField('作成日時')
description = models.TextField('説明')
def __str__(self):
return self.title
モデル
カスタムのユーザーとパーミッション¶
Django のパーミッションフレームワークをカスタムのユーザーモデルに簡単に取り入れられるように用意されているのが、Django の PermissionsMixin
です。これはユーザーモデルの階層に取り入れることができる抽象モデルで、Django のパーミッションモデルをサポートするのに必要なすべてのメソッドとデーターベースのフィールドを使えるようにしてくれます。
PermissionsMixin
は、以下のメソッドと属性を提供します。class models.
PermissionsMixin
¶is_superuser
¶
真偽値です。明示的に与えられない場合でも、ユーザーがが全てのパーミッションを持っているかどうかを示します。get_user_permissions
(obj=None)¶New in Django 3.0.
Returns a set of permission strings that the user has directly.
If obj
is passed in, only returns the user permissions for this specific object.get_group_permissions
(obj=None)¶
ユーザがグループを通して持つパーミッションの文字列のセットを返します。
obj
が渡されたとき、指定されたオブジェクトに対するグループパーミッションのみを返します。get_all_permissions
(obj=None)¶
ユーザがグループおよびユーザパーミッションを通して持つパーミッションの文字列のセットを返します。
obj
が渡された場合、指定されたオブジェクトに対するパーミッションのみを返します。has_perm
(perm, obj=None)¶
ユーザーが指定したパーミッションを持っている場合、True
を返します。ここで、perm
は "<app label>.<permission codename>"
という形式で指定します (permissions を参照)。もし、User.is_active
と is_superuser
が両方とも True
だった場合、このメソッドは常に True
を返します。
obj
が渡された場合、このメソッドはモデルに対するパーミッションのチェックを行わず、指定されたオブジェクトに対して行います。has_perms
(perm_list, obj=None)¶
ユーザーが指定したパーミッションを持っている場合、True
を返します。ここで、perm
は "<app label>.<permission codename>"
という形式で指定します。もし、User.is_active
と is_superuser
が両方とも True
だった場合、このメソッドは常に True
を返します。
obj
が渡された場合、このメソッドは指定されたオブジェクトに対してパーミッションのチェックを行い、モデルに対しては行いません。has_module_perms
(package_name)¶
Returns True
if the user has any permissions in the given package (the Django app label). If User.is_active
and is_superuser
are both True
, this method always returns True
.
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField('メールアドレス', unique=True)
first_name = models.CharField(('姓'), max_length=30)
last_name = models.CharField(('名'), max_length=30)
description = models.TextField('自己紹介', default="", blank=True)
image = models.ImageField(upload_to='images', verbose_name='プロフィール画像', null=True, blank=True)
view.py
allauth.accountからインポートしたviewsのLoginViewを引数
class LoginView(views.LoginView):
template_name = 'accounts/login.html'
from django.shortcuts import render, redirect
from django.views import View
from accounts.models import CustomUser
from accounts.forms import ProfileForm,SignupUserForm
from allauth.account import views
from django.contrib.auth.mixins import LoginRequiredMixin
# Create your views here.
class ProfileView(LoginRequiredMixin,View):
def get(self, request, *args, **kwargs):
user_data = CustomUser.objects.get(id=request.user.id)
return render(request, 'accounts/profile.html', {
'user_data': user_data,
})
class ProfileEditView(LoginRequiredMixin,View):
def get(self, request, *args, **kwargs):
user_data = CustomUser.objects.get(id=request.user.id)
form = ProfileForm(
request.POST or None,
initial={
'first_name': user_data.first_name,
'last_name': user_data.last_name,
'department': user_data.department,
'description': user_data.description,
'image': user_data.image,
}
)
return render(request, 'accounts/profile_edit.html', {
'form': form,
'user_data': user_data
})
def post(self, request, *args, **kwargs):
form = ProfileForm(request.POST or None)
if form.is_valid():
user_data = CustomUser.objects.get(id=request.user.id)
user_data.first_name = form.cleaned_data['first_name']
user_data.last_name = form.cleaned_data['last_name']
user_data.description = form.cleaned_data['description']
if request.FILES.get('image'):
user_data.image = request.FILES.get('image')
user_data.save()
return redirect('profile')
return render(request, 'accounts/profile.html', {
'form': form
})
class LoginView(views.LoginView):
template_name = 'accounts/login.html'
class LogoutView(views.LogoutView):
template_name = 'accounts/logout.html'
def post(self, *args, **kwargs):
if self.request.user.is_authenticated:
self.logout()
return redirect('/')
class SignupView(views.SignupView):
template_name = 'accounts/signup.html'
form_class = SignupUserForm
html
profile.html
<tr>
<th class="header">プロフィール画像</th>
<td class="data">
{% if user_data.image %}
<img src='/{{ user_data.image.url }}' width=100px>
{% endif %}
</td>
</tr>
<tr>
<th class="header">名前</th>
<td class="data">{{ user_data.first_name }} {{ user_data.last_name }}</td>
</tr>
<tr>
<th class="header">メールアドレス</th>
<td class="data">{{ user_data.email }}</td>
</tr>
<tr>
<th class="header">自己紹介</th>
<td class="data">{{ user_data.description }}</td>
</tr>
profile_edit.html
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<table class="profile_table mb-4">
<tbody>
<tr>
<th class="header">プロフィール画像</th>
<td class="data">
{{ form.image }}
</td>
</tr>
<tr>
<th class="header">名前</th>
<td class="data form_wrap form_wrap-2">
{% render_field form.first_name class="form-control" placeholder="姓" %}
{% render_field form.last_name class="form-control" placeholder="名" %}
</td>
</tr>
<tr>
<th class="header">自己紹介</th>
<td class="data">
{% render_field form.description class="form-control" placeholder="自己紹介" %}
</td>
</tr>
</tbody>
</table>
予約モデル
from django.utils import timezone
class Booking(models.Model):
staff = models.ForeignKey(Staff, verbose_name='スタッフ', on_delete=models.CASCADE)
first_name = models.CharField('姓', max_length=100, null=True, blank=True)
last_name = models.CharField('名', max_length=100, null=True, blank=True)
tel = models.CharField('電話番号', max_length=100, null=True, blank=True)
remarks = models.TextField('備考', default="", blank=True)
start = models.DateTimeField('開始時間', default=timezone.now)
end = models.DateTimeField('終了時間', default=timezone.now)
def __str__(self):
start = timezone.localtime(self.start).strftime('%Y/%m/%d %H:%M')
end = timezone.localtime(self.end).strftime('%Y/%m/%d %H:%M')
return f'{self.first_name}{self.last_name} {start} ~ {end} {self.staff}'
スタッフリストの作成
style.css
body {
background: #F1F1F1;
display: flex;
flex-flow: column;
min-height: 100vh;
}
main {
flex: 1;
}
.storelist img {
height: 150px;
object-fit: contain;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #F1F1F1;
display: flex;
flex-flow: column;
min-height: 150vh;
}
main {
flex: 1;
}
.card-profile {
width: 700px;
border: 0;
border-radius: 1rem;
box-shadow: 0 0.2rem 0.2rem 0 rgba(0, 0, 0, 0.1);
}
.button {
width: 150px;
}
.btn {
font-size: 80%;
border-radius: 5rem;
font-weight: bold;
}
.profile_table {
width: 100%;
border: 1px solid #D1DBEB;
border-radius: 8px;
border-collapse: separate;
overflow: hidden;
}
.profile_table .header {
width: 200px;
padding: 16px 24px;
text-align: left;
background: #F1F5FA;
}
.profile_table .data {
padding: 16px 24px;
}
.form_wrap {
display: grid;
gap: 16px;
}
.form_wrap-2 {
grid-template-columns: repeat(2, 1fr);
}
.form-control:focus {
box-shadow: none;
}
.card-auth {
width: 400px;
border: 0;
border-radius: 1rem;
box-shadow: 0 0.2rem 0.2rem 0 rgba(0, 0, 0, 0.1);
}
.card-title {
margin-bottom: 2rem;
font-size: 1.5rem;
}
.card-body {
padding: 2rem;
}
.form-auth {
width: 100%;
}
.form-label-group {
margin-bottom: 1rem;
}
.form-label-group input {
border-radius: 2rem;
}
.top img {
object-fit: cover;
height: 600px;
}
.overlay {
position: absolute;
}
.title {
font-size: 4rem;
}
.subtitle {
font-size: 2rem;
}
.navbar-nav {
flex-direction: row!important;
}
.nav-color {
color: black;
}
.nav-color:hover {
color: #EE6C4D;
}
.nav-color:after {
content: "";
display: block;
height: 2px;
background: #EE6C4D;
margin-top: 6px;
opacity: 0;
transform: translateY(12px);
transition: all 0.3s ease-in-out;
}
.nav-color:hover:after {
transform: translateY(0px);
opacity: 1;
}
/* store */
.storelist img {
height: 150px;
object-fit: contain;
}
/* index */
#home {
background-image: url(../../../media/images/フリーランスの宿.png);
min-height: 100vh;
}
/* contact */
#image {
background-image: url(../../media/images/お問い合わせ.png);
min-height: 150vh;
}
.contact {
max-width: 500px;
}
.form-control:focus {
border-color: #EE6C4D;
box-shadow: none;
}
.card {
border: none;
}
/* 店舗情報・地図 */
#location {
padding: 4% 0;
}
#location .wrapper {
display: flex;
justify-content: space-between;
}
.location-info {
width: 22%;
}
.location-info p {
padding: 12px 10px;
}
.location-map {
width: 74%;
}
/* SNS */
#sns {
background: #FAF7F0;
padding: 4% 0;
}
#sns .wrapper {
display: flex;
justify-content: space-between;
}
#sns .sub-title {
margin-bottom: 30px;
}
.sns-box {
width: 30%;
}
お問い合わせ作成
ターミナル上でメール送信の確認ができます。
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
URL作成
path('', views.ContactView.as_view(), name='store'),
form.py作成
EmailFieldを使います。
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=30, label='名前')
email = forms.EmailField(max_length=30, label='メールアドレス')
message = forms.CharField(label='メッセージ', widget=forms.Textarea())
views.py
.formsからContactFormをインポートします。
django.core.mailからBadHeaderError, EmailMessageをインポートします。
import textwrapをインポートします。
getとpostに関しては下記記事に詳しく書いています。
先頭の空白を削除するためにtextwrap.deden
関数を使用します。
contact.html
{% csrf_token %}
render_field
地図を表示
- Googleマップで表示したい場所の住所を入力
- 共有ボタンをクリックし、地図を埋め込むをクリック
- カスタムサイズを「800×400」のサイズに設定し、HTMLをコピー
SNSレイアウト
個人アカウントのページは使うことができません。Facebookページ(ビジネスアカウント)を作成しましょう。