エラー

【Django】”__init__() takes 1 positional argument but 2 were given”の対処法。

DjangoでAPIを作成していたら"init() takes 1 positional argument but 2 were given"というエラーが出てしまいました。

その際に解消できた対処法のメモです。

init() takes 1 positional argument but 2 were given

原因のコード

from django.urls import path, include
from .views import ArticleListView

urlpatterns = [
    path('articles/', IndexArticles, name='articles'),
]
class ArticleListView(generics.ListAPIView):
	queryset = Article.objects.all()
	serializer_class = ArticleSerializer
	permission_classes = (AllowAny,)

解決法 → urls.pyに.as_view()を記載する

urls.pyのに.as_view()を記載することで解消されました。

from django.urls import path, include
from .views import ArticleListView

urlpatterns = [
    path('articles/', IndexArticles.as_view(), name='articles'),  # 変更
]

“.as_view()”はどんな時に必要?

ビューには「関数ビュー」と「クラスビュー(class)」の2つがあり、クラスビューの場合は.as_view()の記載が必要なようです。

関数ビュー「.as_view()はいらない」

# 関数ビュー
def IndexView(request):
	
	params = {
		...,
	}
	return render(request, 'pages/index.html', params)
urlpatterns = [
    path('/', IndexView, name='index'),
    ...,
]

クラスビュー「.as_view()が必要」

# クラスビュー
class ArticleListView(generics.ListAPIView):
	queryset = Article.objects.all()
	serializer_class = ArticleSerializer
	permission_classes = (AllowAny,)
urlpatterns = [
    path('articles/', IndexArticles.as_view(), name='articles'),
    ...,
]

まとめ

ビューに「クラスビュー」と「関数ビュー」の2種類あるなんて知りませんでした…。

もっといろいろと触りながら少しずつDjangoの基本を探っていきたいなと思います!

【Django】django-filterが使えない原因と解決法。

Django REST Frameworkのdjango-filterを使って「記事タイトルのキーワード検索するためのフィルタ機能」を作ろうとしていました。

ところが上手くdjango-filterが動かず少しハマってしまいました。

最終的に今回の場合は、一箇所の記述を変えるだけで動作したのですがその時のメモです。

django-filterが動作しない原因・解決法

views内で使用フィルタを指定するfilter_classfilterset_classに変更することで動作しました。

つまり原因はこのviews内のフィルタ指定の書き方が間違っていたみたいです。

ただ、以前作成したサービスではfilter_classでも動作しているので、もしかするとバージョンによる違いなのかもしれません。

動作しなかったコード

class Blog(models.Model):
    title = models.CharField(blank=True, null=True, max_length=255, verbose_name='タイトル')
    created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True, verbose_name='作成日時')
    updated_at = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name='更新日時')
# 【Blog】リスト取得View
class BlogListView(generics.ListAPIView):
    queryset = Blog.objects.all()       # 1: クエリセット
    permission_classes = (AllowAny,)    # 2: アクセス許可範囲を指定
    serializer_class = BlogSerializer   # 3: 利用するSerializer指定
    filter_class = BlogFilter           # 4: 利用するFilter指定


# 【Blog】シリアライザー
class BlogSerializer(serializers.ModelSerializer):

    class Meta:
        model = Blog
        fields = '__all__'


# 【Blog】フィルタ
class BlogFilter(serializers.ModelSerializer):
    title = filters.CharFilter(name="title", lookup_expr='contains')

    class Meta:
        model = Blog
        fields = '__all__'

修正後コード

# 【Blog】リスト取得View
class BlogListView(generics.ListAPIView):
    queryset = Blog.objects.all()       # 1: クエリセット
    permission_classes = (AllowAny,)    # 2: アクセス許可範囲を指定
    serializer_class = BlogSerializer   # 3: 利用するSerializer指定
    filterset_class = BlogFilter        # 【修正箇所】4: 利用するFilter指定


# 【Blog】シリアライザー
class BlogSerializer(serializers.ModelSerializer):

    class Meta:
        model = Blog
        fields = '__all__'


# 【Blog】フィルタ
class BlogFilter(serializers.ModelSerializer):
    title = filters.CharFilter(name="title", lookup_expr='contains')

    class Meta:
        model = Blog
        fields = '__all__'

まとめ

やっぱり常に公式ドキュメントを見る癖をつけないといけないですね。

あと他環境でfilter_classが動作して、今回は動作しなかった詳しい原因がわからないままですが、バージョン管理の重要さについても実感することができました。

【DRF】M2Mを含むインスタンスをcreate()する時に出たエラー。

今回新しくManyToManyフィールド(Tag)を含む投稿(Post)インスタンスを作成しようとしたところエラーが出たのでその解消法メモ。

M2M含むインスタンス作成時に出たエラー

実行したこと

class Tag(models.Model):
    name = models.CharField(blank=True, null=True, max_length=255)

class Post(models.Model):
    title = models.CharField(blank=True, null=True, max_length=255)
    tags = models.ManyToManyField("Tag", through="PostTagRelation", blank=True, null=True)
def create(self, validated_data):
    # 1: validated_dataからtagsを取り出す処理
    tags_data = self.validated_data.pop('tags')

    # 2: Postインスタンスを作成する処理
    post = Post.objects.create(**validated_data)
    
    # 3: tagsを一つずつPostへ追加する処理
    for tag_data in tags_data:
        tag_qs = Tag.objects.filter(id=tag_data['id'])
        if tag_qs.exists():
            tag = tag_qs.first()
        else:
            tag = Tag.objects.create(**tag_data)
			post.tags.add(tag)

    post.save()
    return post

実際のエラー文

TypeError: Direct assignment to the forward side of a many-to-many set is prohibited. Use tags.set() instead.

解決法

def create(self, validated_data):
    # 1: validated_dataからtagsを取り出す処理
    tags_data = self.validated_data.pop('tags')

    # 2: Postインスタンスを作成する処理
    post = Post.objects.create(
        title=validated_data.get('title'),  # 追加: フィールドを指定
    )

    # 3: tagsを一つずつPostへ追加する処理
    for tag_data in tags_data:
        tag_qs = Tag.objects.filter(id=tag_data['id'])
        if tag_qs.exists():
            tag = tag_qs.first()
        else:
            tag = Tag.objects.create(**tag_data)
			post.tags.add(tag)

    post.save()
    return post

【Django】「ImportError: Couldn’t import Django. Are you sure it’s …」のエラーが出た。

Djangoアプリケーションを作ろうと思って、その中でターミナルでmanage.pyを実行しようと思ったところこのエラーが出ました。

今回のエラー文

ImportError: Couldn't import Django. Are you sure it's installed and available on your PYTHONPATH environment variable? Did you forget to activate a virtual environment?

(直訳)
1. [インポートエラー] Django がインポートできません!
2. Djangoはインストールされていますか、そしてそれが 環境変数 PYTHONPATH している場所にありますか?
3. 仮想環境(virtualenv)を開始することを忘れていませんか?

https://teratail.com/questions/130346

エラーの解消法

仮想環境に入って、再度「django」をインストールして実行したら動きました。

Pycharmだと「Preferences」→「Project Interpreter」で仮想環境を指定して後に、ターミナルを再起動しないといけなかったみたいです。

エラーが起きた原因

今回はAnacondaで仮想環境を作って、そこで「python manage.py runserver」を実行しようとしていました。

ところがその仮想環境に「django」がインストールされていなかったためエラーが起きてしまっていたみたいです 。(仮想環境ではなくローカル環境でインストールしてた…)

とても初歩的なミスをしてしまいました。今後、「いま仮想環境に入っているか」をしっかり確認することを注意していこうと思います。