- 投稿日:2020-04-30T23:42:54+09:00
(一部翻訳)GitLabでSquash Commitした時のコミットメッセージ
記事を書いた動機
GitLabのマージリクエストでSquash Commitした時のコミットメッセージが、何が選ばれるのか複雑で分かりにくい!
公式は英語なので読むついでに大事なところだけ日本語に訳しました。意訳が入っています。
内容は本日(2020/04/30)時点のものです。公式(英語)
https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html
大事なところだけ翻訳
Squashされたコミットのコミットメッセージは以下のように決定されます:
- マージリクエスト内のコミットの、一番最初の複数行のコミットメッセージが選ばれます。
- 複数行のコミットメッセージが無かった場合は、マージリクエストのタイトルが選ばれます。
注記:これは最低でも2コミット以上ある場合に適用されます。1コミットしかなくSquashする必要がなかった場合は、コミットメッセージは元のものがそのまま使われます。
マージリクエストでマージする前に、このコミットメッセージは任意に決めることもできます。
(マージボタンの下にあるCollapseを開き、コミットメッセージを入力できるUIが提供していることを示す画像)
注記:この画像の例だと、プロジェクトのリポジトリはマージコミットを使用することになっているため、Squashされたコミットのメッセージの後にマージコミットのメッセージが続きます。
- 投稿日:2020-04-30T16:45:41+09:00
Django Sprint #3 ユーザーモデルのカスタマイズ
- Django Sprint #0 イントロダクション
- Django Sprint #1 環境構築
- Django Sprint #2 新規プロジェクトのスタート
- Django Sprint #3 ユーザーモデルのカスタマイズ
- Django Sprint #4 トップページの作成
- Django Sprint #5 汎用ビューとCRUD 前編
- Django Sprint #6 汎用ビューとCRUD 後編
目次
1. Gitの運用
2. 各種設定
3. モデル概論
4. ユーザーモデルと認証、管理者Gitの運用
Gitは初めて使う人にとっては少し難しく感じるかもしれません。その際は、言葉を追うより、「習うより慣れろ」的に実際に使ってみる方が分かりやすいでしょう。もし難しいようならGitを使わなくても開発はできます。
本チュートリアルは GitHub Flow を少し変更して行います。もしチームの別の運用ルールがあるのなら、そちらを優先させてください。
また、GitをGUI上で(コマンドラインからではなく、視覚的に)操作するには
をおすすめします。
試しに、READMEファイル(このプロジェクトの説明などを記入するファイル)を追加してみましょう。
branchとは
ブランチとは作業履歴を枝分かれさせて記録していくためのものです。試しに、以下のコマンドを叩いてみましょう。
$ git branch * master現在はmasterブランチしかないはずです。
2つのブランチ: master と develop
先ほど見たmasterブランチはプロジェクトのメインのブランチです。これを「常にデプロイ(公開)可能なもの」としましょう。つまり、テストも終わった「完成品」のみが許される神聖な場所です。
一方、普段の開発用のブランチをdevelopとします。これは開発用のメインのブランチで、公開はされません。developブランチを切る
このdevelopブランチを作成しましょう。ブランチを切るのは次のコマンドです。
$ git checkout -b develop Switched to a new branch 'develop'一応ブランチの一覧を見ると、やはり
$ git branch * develop masterこのようになっています。*は今いるブランチを指します。
これをリモートに反映させましょう。$ git push -u origin developオプション
-uにより、ローカルリポジトリの現在のブランチの上流をorigin developに規定します。これを一度設定すると以後は$ git pushのみでリモートのdevelopブランチにプッシュされることになります。
一度GitHubのページでdevelopブランチが正しく生成されているかどうか確認してみましょう。実際の開発フロー
実際の開発はほとんど以下の繰り返しです。
git pullでリモートの変更をローカルに反映git checkout -bでローカルに機能ブランチを作成git addとgit commitで変更をローカルの機能ブランチに反映git pushでローカル機能ブランチをリモートにアップ- pull request を作成し、機能ブランチをdevelopブランチにmergeして良いかレビュー&修正
- GitHub上でmerge
参照 : 『プルリクエストを使った開発プロセス』
機能ブランチの作成
チームメンバーでまだGitに関する何のセットアップもしていない方は
を参照してください。
まず、リモートの変更をローカルに反映させます。
$ git checkout develop $ git pull origin develop次に、機能ブランチを切ります(作成します)。今回はブランチ名を「add-readme」とします。できるだけ、説明的なブランチ名にすると良いでしょう。
$ git checkout -b add-readmeadd & commit
まず、機能を追加・変更・削除します。ここでは、codeディレクトリ直下に、README.mdというファイルを作ります。READMEとはプロジェクトの説明などを書く場所で、主に外部の人がこのプロジェクトがどのようなものなのか理解するために使うものです。ちなみに
.mdはマークダウン記法を表す拡張子です。ファイルを作成し、ここでは内容を以下のように書き換えます。/README.md# Django Sprint This is a tutorial for UTokyo Project Sprint.各自好きなように書いてください。
次に、変更を保存、反映させます。
$ git add -A $ git commit -m "Add README"pull request
作業を完了したら、まずpushします。
$ git push origin add-readme次に、pull request(プルリク)を送り、チームメンバーの確認段階に入ります。詳しい内容や手順は、
を参照してください。
これから
今後、細かいGit関連のコマンドは省略したいと思います。Git関連は慣れが重要なので、README.mdや次の設定変更などで一度使ってみることをおすすめします。
厄介なのは、コンフリクトを起こしたときです。まずGoogleで調べてみて、類似の事例がない場合は遠慮なくご相談ください。
各種設定
settings.py
一度settings.pyを開いてみてみましょう!
settings.pyはこのプロジェクトの「設定」を記述したものです。ここで各種設定を変更することができます。よく使うので、ざっと見ておくと良いでしょう。タイムゾーンの変更
試しにタイムゾーン(時刻帯)を変更してみましょう。まず、settings.pyから以下の記述を探し出してください!
/config/settings.py... TIME_ZONE = 'UTC' ...デフォルトではタイムゾーンは「UTC」、つまりロンドンのそれに設定されています。これを東京のものに変更するには以下のように書き換えてみれば良いです。
/config/settings.py... TIME_ZONE = 'Asia/Tokyo' ...言語の変更
続いて言語を変更してみましょう。デフォルトでは「en-us」、つまり、英語(アメリカ)に設定されています。Djangoはたくさんの言語をサポートしており、幸いにも日本語にも対応しています。日本語に変更するには以下のように書き換えれば良いです。
/config/settings.py... LANGUAGE_CODE = 'ja' ...モデル概論
MVCモデルとは
MVCモデルとは
- モデル(Model): データベースとコントローラーの間で、データ処理を行うファイル群
- ビュー(View): ブラウザとコントローラーの間で、リクエストの取得やHTMLなどのファイルの出力を担うファイル群
- コントローラー(Controller): 中枢でモデルとビューからデータを受け取り、処理して返すファイル群
詳しくは
を参照してください。
DjangoにおけるMVCモデル
かなりややこしい表記の違いがあります。
- モデル(Model): Djangoではモデル(Model)と呼ばれる
- ビュー(View): Djangoではテンプレート(Template)と呼ばれる
- コントローラー(Controller): Djangoではビュー(View)と呼ばれる
以後、ビューと書けば「コントローラー」を意味していると考えてください。
具体的なモデルファイル
具体的なモデルのファイルを見てみましょう。ここでは、サンプルとして他のプロジェクトから持ってきたものを見てみましょう。
... class Post(models.Model): author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) title = models.CharField(max_length=200) text = models.TextField() created_date = models.DateTimeField(default=timezone.now) published_date = models.DateTimeField(blank=True, null=True)ブログアプリの記事のモデルをイメージしてください。この
class PostはExcelのシートのようなものです。一方、authorやtitleなどはExcelのシートの各カラム(列)に対応します。CharFieldなどはそのデータの型を表します。もしもモデルと別のモデルを組み合わせたいときはForeignKeyなどを使います。上の例ではauthorはPostモデルとユーザーモデルを繋ぐものです。このようなフィールドの種類とオプションは以下のドキュメントを参考にしてください。ユーザーモデルと認証、管理者
Djangoにはユーザーモデルとその認証機能(ユーザー名、メールアドレス、パスワード)がデフォルトで付属しています。(パスワードはハッシュ化されて保存されます。)その点他のフレームワークよりも楽で、堅牢です。
ただし、注意しなければならないのは最初にユーザーモデルのカスタマイズを行わないと後から変更するのが難しくなる点です。不可能ではないのですが、面倒です。そこで、本チュートリアルでは最初に少しだけカスタマイズしておき、その後のカスタマイズを容易にしておきます。(実際に公式ドキュメントでも強く推奨されています。)
新しいアプリケーションの作成
まず、アプリケーションを作成します。Djangoにおけるアプリケーションとは、プロジェクトを構成する機能的な単位の一つです。再利用を簡単にするために機能ごとに分けることができます。しかし、本チュートリアルでは「cms」(Contents Management System)というアプリケーションにひとまず全てまとめようと思います。
$ docker-compose run --rm web django-admin startapp cmsこれで、次のようなディレクトリ構造になったか確認してください。
code ├─ requirements.txt ├─ README.md ├─ manage.py ├─ config │ ├─ __init__.py │ ├─ asgi.py │ ├─ settings.py │ ├─ urls.py │ └─ wsgi.py ├─ cms │ ├─ migrations │ ├─ __init__.py │ ├─ admin.py │ ├─ apps.py │ ├─ models.py │ ├─ tests.py │ └─ views.py ├─ docker-compose.yml └─ Dockerfile先ほどみたような
models.pyやviews.pyが見えていますね!実際にはこれらのファイルを書き換えることでWebを構築します。(ファイルを追加する場合もあります。)ここで忘れずにアプリケーションを追加したという事実をDjangoに伝えましょう。
/settings.py... INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'cms.apps.CmsConfig', #Added ] ...tips : プロジェクト名を「config」にしたのは、このプロジェクトファイル全体を一つのアプリケーション(Djangoのアプリケーションとは別)と見たときに、「config」ディレクトリに設定に関する情報が含まれているからです。
models.pyの変更
モデルのファイルを書き換えますが、現時点ではコピペで構わないと思います。(もう少し簡潔な方法があるかもしれませんが、動作が確認されているのでひとまずこれで...)
/cms/models.pyfrom django.contrib.auth.base_user import ( AbstractBaseUser, BaseUserManager, ) from django.contrib.auth.models import PermissionsMixin from django.contrib.auth.validators import UnicodeUsernameValidator from django.core.mail import send_mail from django.db import models from django.utils import timezone from django.utils.translation import gettext_lazy as _ # User-related class UserManager(BaseUserManager): use_in_migrations = True def _create_user(self, email, password, **extra_fields): """ Create and save a user with the given email and password. """ if not email: raise ValueError('Users must have an email address') user = self.model( email=self.normalize_email(email), **extra_fields, ) user.set_password(password) user.save(using=self._db) return user def create_user(self, email=None, password=None, **extra_fields): extra_fields.setdefault('is_staff', False) extra_fields.setdefault('is_superuser', False) return self._create_user(email, password, **extra_fields) def create_superuser(self, email=None, password=None, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) if extra_fields.get('is_staff') is not True: raise ValueError('Superuser must have is_staff=True.') if extra_fields.get('is_superuser') is not True: raise ValueError('Superuser must have is_superuser=True.') return self._create_user(email, password, **extra_fields) class AbstractUser(AbstractBaseUser, PermissionsMixin): username_validator = UnicodeUsernameValidator() username = models.CharField( _('username'), max_length=150, unique=True, help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'), validators=[username_validator], error_messages={ 'unique': _("A user with that username already exists."), }, ) first_name = models.CharField(_('first name'), max_length=150, blank=True) last_name = models.CharField(_('last name'), max_length=150, blank=True) email = models.EmailField(_('email address'), unique=True) is_staff = models.BooleanField( _('staff status'), default=False, help_text=_('Designates whether the user can log into this admin site.'), ) is_active = models.BooleanField( _('active'), default=True, help_text=_( 'Designates whether this user should be treated as active. ' 'Unselect this instead of deleting accounts.' ), ) date_joined = models.DateTimeField(_('date joined'), default=timezone.now) objects = UserManager() EMAIL_FIELD = 'email' USERNAME_FIELD = 'username' REQUIRED_FIELDS = ['email'] class Meta: verbose_name = _('user') verbose_name_plural = _('users') abstract = True def __str__(self): return self.email def clean(self): super().clean() self.email = self.__class__.objects.normalize_email(self.email) def email_user(self, subject, message, from_email=None, **kwargs): """Send an email to this user.""" send_mail(subject, message, from_email, [self.email], **kwargs) class User(AbstractUser): class Meta(AbstractUser.Meta): swappable = "AUTH_USER_MODEL"(筆写追記)もしかしたら既存のAbstractUserを継承したUserを作れば済むかもしれません
実際にはこれが裏で記述されています。それを上書きしたような形です。具体的には、
username: ユーザー名first_name: 名last_name: 姓date_joined: 参加した日時is_staff: 管理者権限を持つか?is_active: 有効なアカウントか?のカラムが設定されました。
最後に、「これをユーザーモデルとする」ことをDjangoに伝えます。settings.pyの最後の部分に
/settings.py... # Custom AUTH_USER_MODEL = 'cms.User'を付け足せば完了です。これ以降任意のタイミングでマイグレーションして大丈夫です。
admin.py
Djangoには管理者用ページが予め用意されています。これのおかげで、実際にコンソールをいじったり、SQL文を書くことなく、データを追加したり、更新したり、削除したりできます。
とりあえず、admin.pyを以下のように書き換えて、ユーザーモデルの変更を反映させましょう。
/cms/admin.pyfrom django import forms from django.contrib import admin from django.contrib.auth.models import Group from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.forms import ReadOnlyPasswordHashField from .models import ( User, ) class UserCreationForm(forms.ModelForm): """A form for creating new users. Includes all the required fields, plus a repeated password.""" password1 = forms.CharField(label='Password', widget=forms.PasswordInput) password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput) class Meta: model = User fields = ('email',) def clean_password2(self): # Check that the two password entries match password1 = self.cleaned_data.get("password1") password2 = self.cleaned_data.get("password2") if password1 and password2 and password1 != password2: raise forms.ValidationError("Passwords don't match") return password2 def save(self, commit=True): # Save the provided password in hashed format user = super().save(commit=False) user.set_password(self.cleaned_data["password1"]) if commit: user.save() return user class UserChangeForm(forms.ModelForm): """A form for updating users. Includes all the fields on the user, but replaces the password field with admin's password hash display field. """ password = ReadOnlyPasswordHashField() class Meta: model = User fields = ('email', 'password', 'is_active', 'is_staff',) def clean_password(self): # Regardless of what the user provides, return the initial value. # This is done here, rather than on the field, because the # field does not have access to the initial value return self.initial["password"] class UserAdmin(BaseUserAdmin): # The forms to add and change user instances form = UserChangeForm add_form = UserCreationForm # The fields to be used in displaying the User model. # These override the definitions on the base UserAdmin # that reference specific fields on auth.User. list_display = ('email', 'is_staff') list_filter = ('is_staff',) fieldsets = ( (None, {'fields': ('email', 'password')}), ('Personal info', {'fields': ('username', 'last_name', 'first_name')}), ('Permissions', {'fields': ('is_staff',)}), ) # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin # overrides get_fieldsets to use this attribute when creating a user. add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('email', 'username', 'password1', 'password2'), }), ) search_fields = ('email',) ordering = ('email',) filter_horizontal = () # Now register the new UserAdmin... admin.site.register(User, UserAdmin) # ... and, since we're not using Django's built-in permissions, # unregister the Group model from admin. admin.site.unregister(Group)管理者は次のコマンドで生成できます。
$ docker-compose run --rm web python manage.py createsuperuserその後にユーザー名、メールアドレス(架空で良い)、パスワードを要求されるので、指示通りに設定します。(これは後から使うので忘れないでください。)
マイグレーション
最後のステップはモデルが書き換わったことをデータベースに伝えることです。これをマイグレーションと良い、Djangoでは通常
- マイグレーションファイルの作成
- マイグレーションファイルの反映
の2ステップからなります。
$ docker-compose run --rm web python manage.py makemigrations $ docker-compose run --rm web python manage.py migrateこれで、初期設定は完了です!
管理者画面
http://localhost:8000/admin/ にアクセスすると、管理者のログイン画面になると思います。そこで、先ほど設定した「ユーザー名」と「パスワード」を入力すると、このような画面になるはずです。
さて、このユーザー「追加」をクリックすると、簡単にユーザーが追加できます。一度、サンプルユーザーを追加してみましょう。
参照
- Django Sprint #0 イントロダクション
- Django Sprint #1 環境構築
- Django Sprint #2 新規プロジェクトのスタート
- Django Sprint #3 ユーザーモデルのカスタマイズ
- Django Sprint #4 トップページの作成
- Django Sprint #5 汎用ビューとCRUD 前編
- Django Sprint #6 汎用ビューとCRUD 後編
- Django Sprint Appendix Docker関連
- Django Sprint Appendix 各種実装まとめ
- Django Sprint Appendix モデルとデータベース
- Django+PostgreSQLのアプリケーションをAWSのElastic Beanstalkにデプロイする (UTokyo Project Sprint 用)
- Django+MySQLのアプリケーションをAWSのElastic Beanstalkにデプロイする (UTokyo Project Sprint 用)
- 投稿日:2020-04-30T15:50:38+09:00
【エラー解決】SSH接続したにも関わらず push,pull するときに毎回passphraseを求められる問題
追記:200430
ご指摘いただきpassword→passphraseに訂正しました。【問題】
SSH接続したにも関わらず
push,pull するときに毎回passwordpassphraseを求められる問題【原因】
SSH接続を設定したとき
$ ssh-keygen
この後に表示される
Enter passphrase (empty for no passphrase):
でenterを押すべきところ、
passwordpassphraseを入力していた。【解決方法】
$ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/Users/${user_name}/.ssh/id_rsa): /Users/${user_name}/.ssh/id_rsa already exists. Overwrite (y/n)?$y #enter×2 Enter passphrase (empty for no passphrase): Enter same passphrase again: この後 enter×2回 $ cat ~/.ssh/id_rsa.pub #SSH 鍵の公開鍵をコピー #GitHubのPersonal settingsからSSH keysに情報を登録。参考にした記事(いつもありがとうございます)
- 投稿日:2020-04-30T15:50:38+09:00
【エラー解決】SSH接続したにも関わらず push,pull するときに毎回passwordを求められる問題
【問題】
SSH接続したにも関わらず
push,pull するときに毎回passwordを求められる問題【原因】
SSH接続を設定したとき
$ ssh-keygen
この後に表示される
Enter passphrase (empty for no passphrase):
でenterを押すべきところ、
passwordを入力してしまった。【解決方法】
$ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/Users/${user_name}/.ssh/id_rsa): /Users/${user_name}/.ssh/id_rsa already exists. Overwrite (y/n)?$y #enter×2 Enter passphrase (empty for no passphrase): Enter same passphrase again: この後 enter×2回 $ cat ~/.ssh/id_rsa.pub #SSH 鍵の公開鍵をコピー #GitHubのPersonal settingsからSSH keysに情報を登録。参考にした記事(いつもありがとうございます)
