简体   繁体   English

如何使用 Factory_Boy 创建管理员用户?

[英]How Can You Create an Admin User with Factory_Boy?

I'm a relative Django beginner and just started doing some testing for my projects.我是一个相对的 Django 初学者,刚刚开始为我的项目做一些测试。 What I want to do is build a functional test with selenium that logs into the Django Admin site.我想要做的是使用 selenium 构建一个登录到 Django 管理站点的功能测试。

I first followed this tutorial http://www.tdd-django-tutorial.com/tutorial/1/ and used fixtures and dumpdata to make the admin account info available for the testing app (which creates a new database).我首先遵循本教程http://www.tdd-django-tutorial.com/tutorial/1/并使用夹具和转储数据使管理员帐户信息可用于测试应用程序(创建新数据库)。 This works fine.这工作正常。

I then wanted to see if I can do the same using factory-boy to replace the fixtures.然后我想看看我是否可以使用factory-boy来更换固定装置。 Factory boy works by instantiating the necessary object within the tests.py file which seems cleaner to me. Factory boy 通过实例化 test.py 文件中的必要对象来工作,这对我来说似乎更清晰。 Somehow I can't get this to work and the Factory_boy documentation is not too helpful...不知何故,我无法让它工作,F​​actory_boy 文档也没有太大帮助......

Here is my tests.py这是我的tests.py

from django.test import LiveServerTestCase
from django.contrib.auth.models import User
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import factory

class UserFactory(factory.Factory):
    FACTORY_FOR = User

    username = 'jeff'
    password = 'pass'
    is_superuser = True

class AdminTest(LiveServerTestCase):

    def setUp(self):
        self.browser = webdriver.Firefox()

    def tearDown(self):
        self.browser.quit()

    def test_if_admin_login_is_possible(self):
        jeff = UserFactory.create()

        # Jeff opens the browser and goes to the admin page
        self.browser = webdriver.Firefox()
        self.browser.get(self.live_server_url + '/admin/')

        # Jeff sees the familiar 'Django Administration' heading
        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('Django administration', body.text)

        # Jeff types in his username and password and hits return
        username_field = self.browser.find_element_by_name('username')
        username_field.send_keys(jeff.username)
        password_field = self.browser.find_element_by_name('password')
        password_field.send_keys(jeff.password)
        password_field.send_keys(Keys.RETURN)

        # Jeff finds himself on the 'Site Administration' page
        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('Site administration', body.text)

        self.fail('Fail...')

This fails to log in as somehow it doesn't create a valid admin account.这无法登录,因为它没有创建有效的管理员帐户。 How can I do that using factory-boy?我怎么能用工厂男孩做到这一点? Is it possible or do I need to use fixtures for that?有可能还是我需要为此使用固定装置?

(In this post some people suggested fixtures are necessary but factory boy didn't come up: How to create admin user in django tests.py . I also tried the solution suggested at the bottom in the same answer: https://stackoverflow.com/a/3495219/1539688 . It didn't work for me...) (在这篇文章中,有些人建议夹具是必要的,但工厂男孩没有出现: 如何在 django tests.py 中创建管理员用户。我还尝试了在同一答案底部建议的解决方案: https://stackoverflow。 com/a/3495219/1539688 。它对我不起作用......)

If you subclass factory.DjangoModelFactory it should save the user object for you.如果你子类 factory.DjangoModelFactory 它应该为你保存用户对象。 See the note section under PostGenerationMethodCall .请参阅PostGenerationMethodCall下的注释部分。 Then you only need to do the following:那么您只需要执行以下操作:

class UserFactory(factory.DjangoModelFactory):
    FACTORY_FOR = User

    email = 'admin@admin.com'
    username = 'admin'
    password = factory.PostGenerationMethodCall('set_password', 'adm1n')

    is_superuser = True
    is_staff = True
    is_active = True

I am using Django 1.11 (I can bet it will work in Django 2+) and factory_boy 2.11.1.我正在使用 Django 1.11(我敢打赌它会在 Django 2+ 中工作)和 factory_boy 2.11.1。 This was pretty simple:这很简单:

import factory
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import User


class SuperUserFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = User

    first_name = factory.Faker('first_name')
    last_name = factory.Faker('last_name')
    username = factory.Faker('email')
    password = factory.LazyFunction(lambda: make_password('pi3.1415'))
    is_staff = True
    is_superuser = True

In this example, all users will have password 'pi3.1415' change it accordingly if you want something different, or you can even use password = factory.Faker('password') to generate a random password (however, it should be something you are able to figure out. Otherwise, it will be very hard to log in).在这个例子中,如果你想要不同的东西,所有用户都会有密码'pi3.1415'改变它,或者你甚至可以使用password = factory.Faker('password')来生成一个随机密码(但是,它应该是能搞清楚,不然登录会很困难)。

Example creating a superuser创建超级用户的示例

>>> user = SuperUserFactory.create()
>>> user.username # the following output will be different in your case
amber60@hotmail.com

Use the email you got from user.username and the password 'pi3.1415' to log in in the admin.使用您从user.username获得的电子邮件和密码'pi3.1415'登录管理员。

What if the user has reverse foreign keys associated?如果用户关联了反向外键怎么办?

Simple, let's say you have a model Profile which has a foreign key to your User model.很简单,假设您有一个模型Profile ,其中包含指向您的User模型的外键。 Then you have to add the following classes:然后你必须添加以下类:

class Profile(models.Model):
    user = models.OneToOneField(User)
    visited = models.BooleanField(default=False)


# You need to set the foreign key dependency using factory.SubFactory
class ProfileFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = Profile

    user = factory.SubFactory(UserFactory)


# use a RelatedFactory to refer to a reverse ForeignKey 
class SuperUserFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = User

    first_name = factory.Faker('first_name')
    last_name = factory.Faker('last_name')
    username = factory.Faker('email')
    password = factory.LazyFunction(lambda: make_password('pi3.1415'))
    is_staff = True
    is_superuser = True
    profile = factory.RelatedFactory(ProfileFactory, 'user', visited=True)

That's it, use the same logic in the example to create your superuser.就是这样,使用示例中的相同逻辑来创建您的超级用户。

I'm assuming you're working on the http://www.tdd-django-tutorial.com tutorial because that's where I got stuck as well.我假设您正在编写http://www.tdd-django-tutorial.com教程,因为那也是我卡住的地方。 You probably figured this out by now, but for the next person, here's the code that worked for me, the trick was adding the _prepare method to ensure password is encrypted, and setting all the flags to true (This was done with Django 1.5.1, if you're using an earlier version, change the User model imports)您现在可能已经想通了,但是对于下一个人,这是对我有用的代码,诀窍是添加 _prepare 方法以确保密码已加密,并将所有标志设置为 true(这是使用 Django 1.5 完成的。 1、如果您使用的是较早版本,请更改用户模型导入)

from django.test import LiveServerTestCase
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

import factory
from django.contrib.auth import get_user_model
User = get_user_model()


class UserFactory(factory.DjangoModelFactory):
    FACTORY_FOR = User

    email = 'admin@admin.com'
    username = 'admin'
    password = 'adm1n'

    is_superuser = True
    is_staff = True
    is_active = True

    @classmethod
    def _prepare(cls, create, **kwargs):
        password = kwargs.pop('password', None)
        user = super(UserFactory, cls)._prepare(create, **kwargs)
        if password:
            user.set_password(password)
            if create:
                user.save()
        return user

class PollsTest(LiveServerTestCase):

    def setUp(self):
        self.browser = webdriver.Firefox()
        self.browser.implicitly_wait(3)
        self.user = UserFactory.create()


    def tearDown(self):
        self.browser.quit()

    def test_can_create_new_poll_via_admin_site(self):
        self.browser.get(self.live_server_url+'/admin/')

        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('Django administration', body.text)

        username_field = self.browser.find_element_by_name('username')
        username_field.send_keys(self.user.username)

        password_field = self.browser.find_element_by_name('password')
        password_field.send_keys('adm1n')
        password_field.send_keys(Keys.ENTER)

        body = self.browser.find_element_by_tag_name('body')
        self.assertIn('Site administration', body.text)

        polls_links = self.browser.find_element_by_link_text('Polls')
        self.assertEqual(len(polls_links), 2)


        self.fail('Finish the test!')

Creating admin users:创建管理员用户:

You can add a Params declaration that provides flexibility to quickly create an admin user or a normal user.您可以添加 Params 声明,以灵活地快速创建管理员用户或普通用户。

https://factoryboy.readthedocs.io/en/latest/introduction.html?highlight=class%20Params#altering-a-factory-s-behaviour-parameters-and-traits https://factoryboy.readthedocs.io/en/latest/introduction.html?highlight=class%20Params#altering-a-factory-s-behaviour-parameters-and-traits

Setting raw passwords:设置原始密码:

In order to ensure that the password param gets set as a raw unencrypted value, you can use Django's set_password to set a raw password after the initial save in the _create classmethod override.为了确保将密码参数设置为原始未加密值,您可以在 _create 类方法覆盖中初始保存后使用 Django 的 set_password 设置原始密码。

https://docs.djangoproject.com/en/2.1/ref/contrib/auth/#django.contrib.auth.models.User.set_password https://docs.djangoproject.com/en/2.1/ref/contrib/auth/#django.contrib.auth.models.User.set_password

class UserFactory(factory.django.DjangoModelFactory):
    first_name = factory.Faker('first_name')
    last_name = factory.Faker('last_name')
    username = factory.Sequence(lambda n: 'demo-user-%d' % n)
    is_staff = False
    is_superuser = False
    password = 'secret'

    @factory.lazy_attribute
    def email(self):
        return '%s@test.com' % self.username

    class Meta:
        model = User

    class Params: 
        # declare a trait that adds relevant parameters for admin users
        flag_is_superuser = factory.Trait(
            is_superuser=True,
            is_staff=True,
            username = factory.Sequence(lambda n: 'admin-%d' % n),
        )

    @classmethod
    def _create(cls, model_class, *args, **kwargs):
        password = kwargs.pop("password", None)
        obj = super(UserFactory, cls)._create(model_class, *args, **kwargs)
        # ensure the raw password gets set after the initial save
        obj.set_password(password)
        obj.save()
        return obj

Usage:用法:

# This admin user can log in as "admin-1", password "secret"
admin = UserFactory.create(flag_is_superuser=True)

# This regular user can log in as "userABC", password "secretABC"
user = UserFactory.create(username="userABC", password="secretABC")

Using factory-boy v2.11.1 and Django v1.11.6使用 factory-boy v2.11.1 和 Django v1.11.6

I had a similar problem when created a user.创建用户时我遇到了类似的问题。 Django hash passwords when creates a user and you save the password using DjangoFactory without hash.创建用户时 Django 散列密码,您使用 DjangoFactory 保存密码而不使用散列。 On login Django checks password that you send with stored hash one.登录时,Django 会检查您使用存储的哈希值发送的密码。 On this step verification fails as you check not hashed password with a not hashed password.在此步骤验证失败,因为您使用未散列的密码检查未散列的密码。 Here is an example how I fixed this in my code:这是我如何在代码中修复此问题的示例:

from django.contrib.auth.hashers import make_password
from factory import DjangoModelFactory, Sequence



class UserFactory(DjangoModelFactory):
    class Meta:
        model = User
        django_get_or_create = ('username', 'password')

    username = Sequence(lambda n: 'somename%s' % n)
    password = Sequence(lambda p: 'mysuperpass%s' % p)

    @classmethod
    def _create(cls, model_class, *args, **kwargs):
        """Override the default ``_create`` with our custom call."""
        kwargs['password'] = make_password(kwargs['password'])
        return super(UserFactory, cls)._create(model_class, *args, **kwargs)

I take password that was generated using Sequence and hash it using Django make_password method.我获取使用 Sequence 生成的密码并使用 Django make_password方法对其进行哈希处理。 In tests you can create a var with not hashed value and create a user with this var.在测试中,您可以创建一个没有散列值的 var 并使用这个 var 创建一个用户。 Example:例子:

password = 'test123'
user = UserFactory(password=my_password)

This way I think you preserve the behavior you expect in your code, this way you have a default value for password and also you can override it with any value you want when calling UserFactory.通过这种方式,我认为您保留了您在代码中期望的行为,这样您就有了密码的默认值,并且您可以在调用 UserFactory 时使用您想要的任何值覆盖它。

class UserFactory(factory.Factory):
    FACTORY_FOR = User

    username = 'jeff'
    password = 'pass'
    is_superuser = True
    
    @classmethod
    def _create(cls, model_class, *args, **kwargs):
        """Create an instance of the model, and save it to the database."""
        if cls._meta.django_get_or_create:
            return cls._get_or_create(model_class, *args, **kwargs)

        manager = cls._get_manager(model_class)
        return manager.create_user(*args, **kwargs) # Just user the create_user method recommended by Django

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM