模块未找到错误:没有名为“ ”的模块
这里是背景信息:
我正在使用Django框架进行开发。我开始编写一些功能,但停下来设置第一个单元测试。
我想测试我在JoBooking.models.py文件中的CustomUser类,这个JoBooking是我在Django项目中创建的一个应用。
于是我在测试文件test_models.py中导入了JoParis.JoBookings.models里的CustomUser。
当我在终端运行python manage.py test或pytest时,遇到了这个错误:ModuleNotFoundError: No module named 'JoParis.JoBooking'。
我已经在settings.py中配置了这个应用(JoBooking),但是还是不行。
包里有init.py文件。
我做了一个简单的测试,检查我的应用的URL是否返回HTTP 200响应,测试是成功的,但我必须去掉从JoParis.JoBookings.models导入CustomUser的那一行。所以测试文件能找到,但当我想测试我的模型时,导入就出问题了。
我卡住了。我不明白这个错误。ModuleNotFoundError: No module named。请给我解释一下这个错误是什么。谢谢。
在test_models.py中
from django.test import TestCase
from ...JoBooking.models import CustomUser
# test inscription avec données valides et obligatoires
class TestCreateUser(TestCase):
def test_create_user(self):
user = CustomUser.objects.create_user(
email='usertest@mail.com',
password='password',
username='',
first_name='user',
last_name='test',
is_superuser=False,
is_staff=False
)
assert CustomUser.objects.filter(email='usertest@mail.com').exists() # verifie si dans la BDD
assert user.first_name == 'user'
assert user.last_name == 'test'
assert user.is_superuser is False # verifie si attribut par défaut pris en compte
assert user.is_staff is False
# test inscription avec données non valides
# test connexion user avec True data
# test connexion user avec False data
# test qui verifie http response = 200
class TestUrl(TestCase):
def test_my_view(self):
response = self.client.get('http://127.0.0.1:8000/')
self.assertEqual(response.status_code, 200)
在settings.py中
from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = x
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'JoBooking',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'JoParis.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, "JoBooking/templates/JoBooking")],
'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',
],
},
},
]
WSGI_APPLICATION = 'JoParis.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'OPTIONS': {
"read_default_file": 'C:\\Users\\murhe\\OneDrive\\Bureau\\EXAM JO\\my.cnf.txt',
},
}
}
# Password validation
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.0/topics/i18n/
LANGUAGE_CODE = 'fr-fr'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/
STATIC_URL = '/static/'
# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
AUTH_USER_MODEL = 'JoBooking.CustomUser'
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'JoBooking.authbackends.EmailAuthBackend',
]
错误信息
: (venv) PS C:\Users\murhe\PycharmProjects\StudiProject\JoParis> pytest
====================================================================== test session starts =======================================================================
platform win32 -- Python 3.11.1, pytest-8.0.1, pluggy-1.4.0
django: version: 5.0.2, settings: JoParis.settings (from ini)
rootdir: C:\Users\murhe\PycharmProjects\StudiProject\JoParis
configfile: pytest.ini
plugins: django-4.8.0
collected 0 items / 2 errors
============================================================================= ERRORS =============================================================================
________________________________________________________ ERROR collecting JoBooking/tests/test_models.py _________________________________________________________
ImportError while importing test module 'C:\Users\murhe\PycharmProjects\StudiProject\JoParis\JoBooking\tests\test_models.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
..\..\..\AppData\Local\Programs\Python\Python311\Lib\importlib\__init__.py:126: in import_module
return _bootstrap._gcd_import(name[level:], package, level)
E ModuleNotFoundError: No module named 'JoParis.JoBooking'
_________________________________________________________ ERROR collecting JoBooking/tests/test_views.py _________________________________________________________
ImportError while importing test module 'C:\Users\murhe\PycharmProjects\StudiProject\JoParis\JoBooking\tests\test_views.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
..\..\..\AppData\Local\Programs\Python\Python311\Lib\importlib\__init__.py:126: in import_module
return _bootstrap._gcd_import(name[level:], package, level)
E ModuleNotFoundError: No module named 'JoParis.JoBooking'
======================================================================== warnings summary ========================================================================
..\venv\Lib\site-packages\_pytest\config\__init__.py:1396
C:\Users\murhe\PycharmProjects\StudiProject\venv\Lib\site-packages\_pytest\config\__init__.py:1396: PytestConfigWarning: Unknown config option: PYTHONPATH
self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
==================================================================== short test summary info =====================================================================
ERROR JoBooking/tests/test_models.py
ERROR JoBooking/tests/test_views.py
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 2 errors during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
================================================================== 1 warning, 2 errors in 0.68s ===============
在views.py中
from django.shortcuts import render, redirect
from django.contrib.auth import login, logout
from .forms import Connexion
from django.contrib.auth.forms import UserCreationForm
from .models import CustomUser, Offre, Reservation, Commande
from .authbackends import EmailAuthBackend
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404
import uuid
# méthode pour créer un formulaire d'inscription (utilisation du formulaire émit par django par défaut)
class CustomSignupForm(UserCreationForm):
class Meta:
model = CustomUser # ici on spécifie qu'on veut ce modèle personnalisé
fields = ('first_name', 'last_name', 'email')
#note : le password est automatiquement intégré dans ce form
# view pour la page d'accueil
def index(request):
return render(request, 'JoBooking/index.html')
# view pour la page des offres
def offres(request):
list_offres = Offre.objects.all() # affiche toutes les offres = plan solo,duo,family ou autre
context = {'offres': list_offres}
return render(request, 'JoBooking/offres.html', context)
# view pour la page d'inscription
def inscription(request):
context = {} # stockage données lors du rendu de page
if request.method == 'POST': # vérification methode de la requête est POST = formulaire soumis
form = CustomSignupForm(request.POST) # création formulaire avec données POST
if form.is_valid(): # vérification de la validité des données
user = form.save() # sauvegarde dans la BDD
user.cle_inscription = uuid.uuid4().hex
user.save()
return redirect('inscription_reussie') # redirection de l'utilisateur vers une autre page
else:
context['errors'] = form.errors # erreur de validation
form = CustomSignupForm() # nouvelle page d'inscription vide donc sans POST
context['form'] = form
return render(request, 'JoBooking/inscription.html', context=context) # renvoie page d'inscription
# view pour l'inscripion réussie d'un user
def inscription_reussie(request):
return render(request, 'inscription_reussie.html')
# view pour la page de connexion
def connexion(request):
message = ""
if request.method == 'POST':
form = Connexion(request.POST) # création formulaire avec données envoyées via POST
if form.is_valid(): # vérification de la validité des données
email = form.cleaned_data['email'] # données nettoyées et récupérées
password = form.cleaned_data['password']
custom_auth = EmailAuthBackend() # implémentation de l'authentification personnalisée
user = custom_auth.authenticate(request, email=email, password=password) # appel de la méthode authenticate
# email = request.POST.get('email')
# password = request.POST.get('password')
if user is not None and password is not None: # signification : si l'user a été trouvée ...
login(request, user, backend='JoBooking.authbackends.EmailAuthBackend') # ya 2 backends d'auth
message = f'Bienvenue {user.first_name} ! Vous êtes connecté.'
return redirect('/') # redirige vers la page d'acceuil
else:
message = 'Identifiants non valides.'
return render(request, 'connexion.html',
context={'message': message}) # user non trouvé, donc retourne page connexion
else:
form = Connexion()
return render(request, 'connexion.html', context={'form': form, 'message': message})
# méthode pour que les users se déconnecte
def deconnexion(request):
logout(request)
return redirect('index')
# méthode pour ajouter à réservation. ( réservation c'est le panier. J'ai volontairement choisi ce terme)
def ajouter_reservation(request, offre_id):
user = request.user
offre = get_object_or_404(Offre, id=offre_id) # ici on récupère l'offre si inexistante,erreur 404
reservation, _ = Reservation.objects.get_or_create(user=user) # récupération du panier
commande, created = Commande.objects.get_or_create(user=user,
offre=offre) # récupération commande
if created: # exemple: une offre n'est pas dans la commande donc elle sera crée
reservation.commandes.add(commande)
reservation.save()
else: # l'offre est deja dans la commande donc on augmente la quantité
commande.quantity += 1
commande.save()
return redirect('offres')
# renvoie à la page de reservation (c'est la page panier, après avoir réserver)
@login_required(login_url='connexion') # connexion nécessaire pour avoir accès a cette page
def reservation(request):
reservation = get_object_or_404(Reservation, user=request.user)
return render(request, 'reservation.html', context={
'commandes': reservation.commandes.all()}) # affiche tous les éléments qu'ya dans la réservation
# methode pour annuler une réservation au complet
def annulation(request):
user_reservation = Reservation.objects.get(user=request.user)
if user_reservation: # si elle existe
user_reservation.commandes.all().delete()
user_reservation.delete() # suppression de tout ce qu'y a dans la réservation qu'on supprime ensuite
return redirect('index') # retourne vers la page d'accueil
def payer(request):
reservation = request.user.reservation
# génération de billets (combinaison des deux clés générées , qr code, nom acheteur + logo ;date de l'evement )
# augmentation de ventes de Offre selon la quantité achetée
for commande in reservation.commandes.all():
offre = commande.offre # récupration du plan dans la commande
offre.ventes += commande.quantity
offre.save()
# paiement dans Reservation devient TRUE (initialement à FALSE)
reservation.paiement = True
reservation.save()
# génération de clé unique pour paiment seulement si payer.
if reservation.paiement == True:
cle_paiement = uuid.uuid4().hex
reservation.cle_paiement = cle_paiement
reservation.save()
# le panier (réservation ) est réinitialisé
reservation.commandes.clear()
# Renvoie à la page de remerciement où on peut telecharger billet
return redirect('remerciements')
def remerciements(request):
return render(request, 'remerciements.html')
'''
在test_views.py中
'''
from django.test import TestCase
from ...JoBooking.models import CustomUser
class TestUrl(TestCase):
def test_my_view(self):
response = self.client.get('http://127.0.0.1:8000/')
self.assertEqual(response.status_code, 200)
'''
另一种测试方法
(venv) PS C:\Users\murhe\PycharmProjects\StudiProject\JoParis> python manage.py test JoBooking
Found 1 test(s).
System check identified no issues (0 silenced).
E
======================================================================
ERROR: JoParis.JoBooking (unittest.loader._FailedTest.JoParis.JoBooking)
----------------------------------------------------------------------
ImportError: Failed to import test module: JoParis.JoBooking
Traceback (most recent call last):
File "C:\Users\murhe\AppData\Local\Programs\Python\Python311\Lib\unittest\loader.py", line 440, in _find_test_path
package = self._get_module_from_name(name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\murhe\AppData\Local\Programs\Python\Python311\Lib\unittest\loader.py", line 350, in _get_module_from_name
__import__(name)
ModuleNotFoundError: No module named 'JoParis.JoBooking'
1 个回答
一般回答:
可以试试下面的方式。
from JoBookings.models import CustomUser
你应该使用相对路径来导入其他脚本。
在 JoParis.JoBookings.models
中:
JoParis
是文件夹名/项目名。
JoBookings
是应用名/应用文件夹名(在项目文件夹内)。
models
是 Python 文件名(在应用文件夹内)。
这些都是相对于当前(导入语句所在的)文件的路径。
所以,要像上面那样导入,JoParis
应该是当前测试脚本的子文件夹。
关于问题的新信息:
对于 C:\Users\murhe\PycharmProjects\StudiProject\JoParis\JoBooking\tests\test_models.py
的位置,可以试试下面的方式。
from ...JoBookings.models import CustomUser
更多信息:
Django 测试: