[PYTHON] Je souhaite créer une base de données de déjeuner [EP1-4] Django pour la première fois

Cet article est une suite.

Dernière fois Deux fois avant Première écriture

La dernière fois, j'ai ajouté la fonction de l'application polls et elle fonctionne comme une application de vote. Enfin, nous avons testé et corrigé le bogue, et la dernière fois était terminée.

Cette fois, je vais jeter un œil au contenu des tutoriels Django 5-7.

Tests complets

J'ai corrigé le bogue was_published_recently dans le test que j'ai créé la dernière fois, mais comme ce correctif de bogue n'a ni origine ni enfant pour affecter d'autres parties, je vais le tester. Ajoutez ce qui suit sous la classe QuestionModelTests dans tests.py.

mysite/polls/tests.py


def test_was_published_recently_with_old_question(self):
    #Représente hier
    time = timezone.now() - datetime.timedelta(days=1, seconds=1)
    old_question = Question(pub_date=time)
    self.assertIs(old_question.was_published_recently(), False)

def test_was_published_recently_with_recent_question(self):
    #Représente la dernière minute
    time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
    recent_question = Question(pub_date=time)
    self.assertIs(recent_question.was_published_recently(), True)

was_published_recently () est plus qu'hier et renvoie maintenant ce qui suit avec True, donc test_was_published_recently_with_old_question () devrait être False et test_was_published_recently_with_recent_question () devrait revenir avec True. Cette prédiction est dans self.assertIs, donc je vais vérifier si elle est vraie.

cmd


(Django) C:\User\mysite>python manage.py test polls
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
...
----------------------------------------------------------------------
Ran 3 tests in 0.002s

OK
Destroying test database for alias 'default'...

Ainsi, Ran 3 tests in 0.002s s'affiche, et vous pouvez voir que 3 tests ont réussi, y compris ceux que j'ai écrits la dernière fois.

Voir le test

Selon le tutoriel, cette application de vote a encore des bugs. Cela chevauche la partie problématique de was_published_recently, mais cela signifie que même les questions qui étaient pub_date à une date et une heure futures seront affichées dans la vue. Cette fois, nous allons construire un environnement de test en utilisant un shell interactif.

cmd


>>> from django.test import Client
>>> # create an instance of the client for our use
>>> client = Client()
>>> from django.urls import reverse
>>> response = client.get(reverse('polls:index'))

Ceci termine l'installation du client de test. Dans le didacticiel, client.get ('/') est exécuté avant cela pour détecter l'erreur, mais il est omis ici. Et que contient la réponse ...

cmd


>>> response.status_code
200
>>> response.content                                                                     </ul>\n'
b'\n    <ul>\n    \n        <li><a href="/polls/1/">What&#x27;s new?</a></li>\n    \n    </ul>\n'
>>> response.context['latest_question_list']
<QuerySet [<Question: What's new?>]>

C'est comme ça. D'où vient le contenu de «response.content»? J'ai lu la documentation officielle, mais je n'ai pas compris. Je sais que je tire quelque chose qui ressemble à html de quelque part, mais inversement, je ne sais que cela.

Voir les améliorations

Jetons un second regard et améliorons la vue. Améliorez pour ne pas afficher les questions enregistrées dans la date et l'heure futures.

mysite/polls/views.py


class IndexView(generic.ListView):
    template_name = 'polls/index.html'
    context_object_name = 'latest_question_list'

#Réécrire cette fonction
    def get_queryset(self):
#En dessous de la différence
         return Question.objects.filter(
        pub_date__lte=timezone.now()
    ).order_by('-pub_date')[:5]

pub_date__lte = timezone.now () pointe vers une pub_date antérieure à la date et à l'heure actuelles. __lte signifie inférieur à égal, ce qui signifie quelque chose comme pub_date <= timezone.now ().

Écrire le code de test de vue

Nous allons créer un code de test comme celui écrit au moment de was_published_recently et le préparer pour un test facile.

mysite/polls/tests.py


def create_question(question_text, days):
   
    time = timezone.now() + datetime.timedelta(days=days)
    return Question.objects.create(question_text=question_text, pub_date=time)


class QuestionIndexViewTests(TestCase):
    def test_no_questions(self):
       
        response = self.client.get(reverse('polls:index'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "No polls are available.")
        self.assertQuerysetEqual(response.context['latest_question_list'], [])

    def test_past_question(self):
        
        create_question(question_text="Past question.", days=-30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(
            response.context['latest_question_list'],
            ['<Question: Past question.>']
        )

    def test_future_question(self):
        
        create_question(question_text="Future question.", days=30)
        response = self.client.get(reverse('polls:index'))
        self.assertContains(response, "No polls are available.")
        self.assertQuerysetEqual(response.context['latest_question_list'], [])

    def test_future_question_and_past_question(self):
        
        create_question(question_text="Past question.", days=-30)
        create_question(question_text="Future question.", days=30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(
            response.context['latest_question_list'],
            ['<Question: Past question.>']
        )

    def test_two_past_questions(self):
      
        create_question(question_text="Past question 1.", days=-30)
        create_question(question_text="Past question 2.", days=-5)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(
            response.context['latest_question_list'],
            ['<Question: Past question 2.>', '<Question: Past question 1.>']
        )

Le code est plus long, mais il contient test_no_question () s'il n'y a pas de questions, test_past_question si la question a été ajoutée dans le passé, etsi la question a été ajoutée ultérieurement. Test_future_question (), test_future_question_and_past_question quand il y a deux questions ajoutées dans le passé et le futur, et test_two_past_questions () quand les deux questions ont été ajoutées dans le passé. Il est écrit. create_question définit le processus de création d'une question dans un test en tant que fonction.

Espérons qu'il réussira comme suit: Une fois que j'ai eu FAIL, même si j'ai corrigé le code après cela, cela ne fonctionnait pas dans le même terminal. Donc, si vous faites une erreur et que vous voulez recommencer, il peut être préférable de monter un autre terminal.

cmd


(Django) C:\User\mysite>python manage.py test polls
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..........
----------------------------------------------------------------------
Ran 8 tests in 0.044s

OK
Destroying test database for alias 'default'...

Test DetailView

L'amélioration du test précédent est que ʻIndexViewn'affichera pas les questions publiées à l'avenir, mais si vous le tirez par URL,DetailViewsera toujours affiché. Nous allons donc résoudre ce problème. Tout d'abord, ajoutez la méthodeget_queryset ()à la classeDetailView de la même manière que l'ajout de ʻIndexView.

mysite/polls/views.py


class DetailView(generic.DetailView):
    model = Question
    template_name = 'polls/detail.html'
    def get_queryset(self):
       
        return Question.objects.filter(pub_date__lte=timezone.now())

Après cela, le didacticiel est en anglais pour une raison quelconque, mais au fur et à mesure qu'il est écrit, j'ai ajouté un test pour vérifier que pub_date montre les précédents et non les futurs. Cela signifie que vous devez.

mysite/polls/test.py


class QuestionDetailViewTests(TestCase):
    def test_future_question(self):
        
        future_question = create_question(question_text='Future question.', days=5)
        url = reverse('polls:detail', args=(future_question.id,))
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_past_question(self):
        
        past_question = create_question(question_text='Past Question.', days=-5)
        url = reverse('polls:detail', args=(past_question.id,))
        response = self.client.get(url)
        self.assertContains(response, past_question.question_text)

Cela peut être interprété de la même manière que pour ʻIndexView. test_future_question vérifie si pub_date n'affiche pas les futures comme DetailView, et test_past_questionvérifie sipub_date montre les anciennes dans DetailView`.

Ceci est également testé et ressemble à ceci: Comme le nombre total de tests jusqu'à présent est de 10, le résultat du test doit être de 10 tests, donc le résultat doit être «Ran 10 tests».

cmd


(Django) C:\User\mysite>python manage.py test polls
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..........
----------------------------------------------------------------------
Ran 10 tests in 0.058s

OK
Destroying test database for alias 'default'...

En regardant jusqu'ici, il semble que vous puissiez faire un test pour ResultView tel quel. Le tutoriel était limité aux suggestions, mais écrivons-en une ici.

mysite/polls/test.py


class QuestionResultViewTests(TestCase):
    def test_future_question(self):
    
        future_question = create_question(question_text='Future question.', days=5)
        url = reverse('polls:results', args=(future_question.id,))
        response = self.client.get(url)
        self.assertEqual(response.status_code, 404)

    def test_past_question(self):
        
        past_question = create_question(question_text='Past Question.', days=-5)
        url = reverse('polls:results', args=(past_question.id,))
        response = self.client.get(url)
        self.assertContains(response, past_question.question_text)

En fait, c'est presque la même chose que quand c'était DetailView. La seule chose qui a changé est ʻurl = reverse ('polls: results', args = ..) `.

Aussi, mettons correctement get_queryset dans ResultsView de views.py. Comme ça

mysite/polls/views.py


class ResultsView(generic.DetailView):
    model = Question
    template_name = 'polls/results.html'
    def get_queryset(self):
       
        return Question.objects.filter(pub_date__lte=timezone.now())

C'est la fin du test du tutoriel, mais il semble que Django dispose également d'une fonction pour tester le comportement de l'écran créé par HTML. C'est vraiment sophistiqué.

Gestion de fichiers statiques

De là, vous pouvez accéder au didacticiel 6. Jusqu'à présent, je n'ai pas utilisé de CSS ou de JavaScript qui apparaissent souvent dans la création de pages Web, mais Django peut bien les utiliser. Django traite ces fichiers comme des fichiers statiques. Il semble que django.contrib.staticfiles puisse gérer de manière centralisée ces fichiers statiques pour chaque application.

Personnaliser la structure de l'application

Créez un dossier static sous polls pour que Django gère les fichiers statiques. Créez un autre dossier polls sous static comme vous l'avez fait lorsque vous avez créé le dossier template. Ensuite, j'écrirai le CSS suivant.

mysite/polls/static/polls/style.css


li a {
    color: green;
}

Ensuite, ajoutez également une référence à CSS dans ʻindex.html`.

mysite/polls/templates/polls/index.html


{% load static %}

<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}">

Écrivez cette déclaration en haut du fichier. {% static%} est une balise de modèle Django qui indique l'emplacement des fichiers statiques.

Quand je le vérifie, ... image.png

C'est définitivement vert, mais la différence est trop subtile (rires)

Ajouter une image d'arrière-plan

Nous ajouterons des paramètres à style.css pour ajouter une image d'arrière-plan.

mysite/polls/static/polls/style.css


body {
    background: white url("images/background.png ") no-repeat;
}

Comme vous pouvez le voir sur le lien, il fait référence à l'image background.png sous le dossier mysite / polls / static / polls / image /, alors mettez le fichier correspondant à l'avance. L'image ne sera pas affichée sans elle. Donc, voici le résultat de l'introduction d'une image appropriée d'Irasutoya. image.png C'était bien affiché!

personnaliser le formulaire d'administration

J'ai confirmé que vous pouvez ajouter Question depuis l'écran de l'administrateur il y a quelques fois (EP1-2), mais dans Django cet écran Vous pouvez même le personnaliser. Dans le didacticiel, essayez le code pour jouer avec l'écran de personnalisation des questions.

mysite/polls/admin.py


from django.contrib import admin
from .models import Question

# Register your models here
class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {'fields': ['question_text']}),
        ('Date information', {'fields': ['pub_date']}),
    ]

admin.site.register(Question, QuestionAdmin)

Les changements sont l'ajout de la classe QuestionAdmin et l'ajout de QuestionAdmin à l'argument de ʻadmin.site.register ()`. Désormais, lorsque vous vous connectez en tant qu'administrateur et que vous entrez dans l'écran de changement de question, les champs sont séparés comme indiqué ci-dessous. image.png Pour le moment, vous ne pouvez voir que la question, mais vous pouvez également voir chaque option qui accompagne la question.

mysite/polls/admin.py


from .models import Choice, Question

admin.site.register(Choice)

Les changements sont que Choice est également ʻimport from models et ʻadmin.site.register (Choice) est ajouté. Quand je le vérifie, tout d'abord, vous pouvez sélectionner Choice au début comme ceci image.png Sélectionnez ensuite «Choix» pour accéder à l'écran de sélection. Ensuite, vous atteindrez l'écran de réglage suivant. image.png C'est encore assez bon, mais comme «Choix» est lié à «Question», cela semble-t-il plus facile à utiliser si vous pouvez le modifier ensemble? Par conséquent, je vais arrêter ʻadmin.site.register (Choice) `et le modifier davantage.

mysite/polls/admin.py


class ChoiceInline(admin.StackedInline):
    model = Choice
    extra = 3


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None,               {'fields': ['question_text']}),
        ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
    ]
    inlines = [ChoiceInline]

Ajoutez la classe ChoiceInline et ajoutez la partie qui la reçoit dans la classe QuestionAdmin. Supprimez ʻadmin.site.register (Choice) `. Ensuite, vous pourrez modifier «Choix» sur l'écran «Question» comme indiqué ci-dessous. image.png

Ensuite, nous allons rendre cet écran, qui est le point précédent de l'écran de réglage «Question», plus facile à utiliser. image.png Je n'ai pas trouvé cela gênant à utiliser, mais j'ajouterai un nouveau code à ʻadmin.pyetmodels.py` pour le rendre encore plus pratique.

mysite/polls/admin.py


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None,               {'fields': ['question_text']}),
        ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
    ]
    inlines = [ChoiceInline]
#La phrase supplémentaire suivante
    list_display = ('question_text', 'pub_date', 'was_published_recently')
    list_filter = ['pub_date']
    search_fields = ['question_text']

mysite/polls/models.py


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    def __str__(self) -> str:
        return self.question_text
    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now
#La phrase supplémentaire suivante
    was_published_recently.admin_order_field = 'pub_date'
    was_published_recently.boolean = True
    was_published_recently.short_description = 'Published recently?'

Le contenu affiché par list_display a été développé, et le filtre de la barre latérale est maintenant efficace avec list_filter, et vous pouvez également rechercher avec search_fields. (Les principaux changements sont dans la boîte) image.png

Personnalisez davantage l'écran de l'administrateur

L'écran administrateur peut également être personnalisé en utilisant le fichier html comme dans le cas de ʻIndexView. Pour être honnête, la valeur par défaut est suffisante, mais il peut être pratique de l'utiliser un jour. Tout d'abord, indiquez à setting.py le chemin du modèle` auquel ce projet fait référence.

mysite/mysite/setting.py


TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')], #Cette ligne! !! !!
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

En fait, la copie et le collage du code de ce didacticiel ont échoué. Dans le tutoriel, il s'agit de `` DIRS ': [BASE_DIR /' templates '], mais cela est considéré comme un calcul de type chaîne de caractères, et bien sûr la chaîne de caractères ne peut pas être divisée, donc une erreur se produira. J'ai donc spécifié le chemin en définissant ʻos.path.join (BASE_DIR, 'templates')], `pour éviter l'erreur. La lecture du tutoriel en anglais ne m'a pas donné de réponse ici. Y a-t-il un problème avec le didacticiel? Je pense que c'est mon oubli, mais ...

Ensuite, tirez le modèle sur l'écran d'administration à partir du fichier source Django. Il existe également un exemple de Template dans le package Django. amical. Vous pouvez vérifier où se trouve le fichier source Django avec le code suivant.

cmd


(Django) C:\Users\mysite> python -c "import django; print(django.__path__)"

Une fois que vous connaissez l'emplacement, ouvrez-le avec l'Explorateur et placez base_site.html sous mysite / templates / admin. Créez un nouveau dossier templates et le dossier ʻadmin en dessous ici. (Base_site.html est dans django / contrib / admin / templates / admindans le fichier source) Puis éditez un peubase_site.html`.

mysite/templates/admin/base_site.html


{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">Écran administrateur</a></h1>
{% endblock %}

Si vous extrayez uniquement la pièce modifiée, elle sera ici. Si vous mettez un caractère dans la balise <a> <\ a>, il apparaîtra en haut de la page. Par défaut, {{site_header | default: _ ('Django administration')}} est inclus.

Vérifiez ensuite si les modifications sont reflétées comme suit. image.png Cela a bien changé! !!

Ceci conclut le didacticiel Django. Il y en avait beaucoup. Pour être honnête, même si vous venez ici, il est toujours impossible de lâcher prise (rires) Cependant, si vous comprenez les bases, je pense qu'une étude plus approfondie fera de grands progrès. C'était un contenu tellement moelleux. Il y a un tutoriel avancé après cela, mais je ne l'ai pas encore fait (rires) Quand vous en avez besoin ...

La prochaine fois, j'étudierai Git en tournant un peu plus Django, alors merci.

Recommended Posts

Je souhaite créer une base de données de déjeuners [EP1] Django pour la première fois
Je souhaite créer une base de données de déjeuner [EP1-4] Django pour la première fois
Je veux créer un Dockerfile pour le moment.
[Salut Py (Partie 1)] Je veux faire quelque chose pour le moment, alors commencez par fixer un objectif.
Je veux déplacer le sélénium pour le moment [pour mac]
Démarrez Django pour la première fois
Je souhaite enregistrer l'heure d'exécution et conserver un journal.
Pour le moment, je veux convertir n'importe quel fichier avec ffmpeg !!
J'ai essayé tensorflow pour la première fois
J'ai essayé de créer un traitement par lots sans serveur pour la première fois avec DynamoDB et Step Functions
Pour la première fois dans Numpy, je vais le mettre à jour de temps en temps
Python: je souhaite mesurer proprement le temps de traitement d'une fonction
Comment utiliser MkDocs pour la première fois
Je souhaite créer facilement un modèle de bruit
J'ai essayé la programmation python pour la première fois.
J'ai essayé Mind Meld pour la première fois
Je souhaite créer un type d'implémentation pouvant être branché
Je souhaite télécharger une application Django sur heroku
Essayez de publier sur Qiita pour la première fois
Je veux créer un bel environnement de développement Python pour mon nouveau Mac
Je veux créer un lecteur de musique et enregistrer de la musique en même temps
Je veux ajouter du silence pendant 1 seconde au début d'un fichier wav
Affichons un template simple idéal pour le premier Django
GTUG Girls + PyLadiesTokyo Meetup Je suis allé au premier machine learning
Je souhaite créer une API qui retourne un modèle avec une relation récursive dans Django REST Framework
Ce que je suis entré dans Python pour la première fois
J'ai essayé Python sur Mac pour la première fois.
Je veux faire défiler la table de décalage Django, mais ...
Enregistrer une tâche dans cron pour la première fois
J'ai essayé python pour la première fois avec heroku
Pour la première fois, j'ai découvert Unix (Linux).
Je souhaite créer manuellement une légende avec matplotlib
AI Gaming Je l'ai essayé pour la première fois
J'ai fait une commande pour attendre que Django démarre jusqu'à ce que la base de données soit prête
C'est bon de participer pour la première fois! Un kit de démarrage hackason que vous souhaitez préparer "avant" de participer au hackason!
Premier Kaggle (kaggle ①)
Kaguru pour la première fois
[TensorFlow] Je souhaite maîtriser l'indexation pour Ragged Tensor
Je veux créer un éditeur de blog avec l'administrateur de django
Une méthode d'étude pour les débutants pour apprendre l'analyse des séries chronologiques
Résumé des points d'achoppement à Django pour la première fois
J'ai essayé l'API Google Cloud Vision pour la première fois
J'ai essayé de créer un bot pour annoncer un événement Wiire
Je souhaite utiliser l'environnement de bureau d'Ubuntu sur Android pour le moment (version Termux)
Je souhaite utiliser l'environnement de bureau d'Ubuntu sur Android pour le moment (version UserLAnd)
Étapes pour créer un projet Django
Qiskit: Je veux créer un circuit qui crée des états arbitraires! !!
Je veux créer un histogramme et superposer la courbe de distribution normale dessus. édition matplotlib
J'ai créé une fonction pour vérifier si le webhook est reçu dans Lambda pour le moment
Je veux créer un fichier pip et le refléter dans le menu fixe
Voir python pour la première fois
Raspberry Pi --1 --Première fois (Connectez un capteur de température pour afficher la température)
Je souhaite créer un service d'apprentissage automatique sans programmation! API Web
Introduction au Deep Learning pour la première fois (Chainer) Reconnaissance de caractères japonais Chapitre 3 [Reconnaissance de caractères à l'aide d'un modèle]
L'histoire du retour au front pour la première fois en 5 ans et de la refactorisation de Python Django
Créez une fonction pour obtenir le contenu de la base de données dans Go
Créer une API REST pour faire fonctionner dynamodb avec le Framework Django REST
Ce que j'ai appris en écrivant une Pull Request Python pour la première fois de ma vie
[Pour les débutants] Je veux obtenir l'index d'un élément qui satisfait une certaine expression conditionnelle