New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Making (or having the option to make) factory.django.FileField non-post-generation #141

Closed
ianawilson opened this Issue Apr 29, 2014 · 3 comments

Comments

Projects
None yet
3 participants
@ianawilson

ianawilson commented Apr 29, 2014

I have a use case where the file is interacted with just before saving to the database (I'm making a sha256 hash of the file and storing this). When using create(), I get an error about the file not existing, because factory boy is building things for the file field after saving. The workaround I have is to use build(), which works, and then save() the instance after all post generation has taken place.

Based on the way your code looks, it seems like it would probably be a little challenging to make FileField optionally post-generation (because it appears to be based on inheritance). Mainly, I'm curious if this is something that you would consider supporting, or if there is a good reason for forcing it to be post-generation. If it seems like an ok idea, I would consider contributing the PR for it, given your thoughts on an implementation.

@rbarrois

This comment has been minimized.

Show comment
Hide comment
@rbarrois

rbarrois Sep 3, 2014

Member

Hi @ianawilson ,

Well, that would be quite interesting.
My main issue here would be to implement it:

  • with the cryptic Django API around FileField
  • while supporting all options of the field (provide a filename, a file object, a file contents, etc.)

The core issue I have is "how do I get a real file-like object and/or a stream of bytes into a Django FileField, with a chosen filename" :)

Member

rbarrois commented Sep 3, 2014

Hi @ianawilson ,

Well, that would be quite interesting.
My main issue here would be to implement it:

  • with the cryptic Django API around FileField
  • while supporting all options of the field (provide a filename, a file object, a file contents, etc.)

The core issue I have is "how do I get a real file-like object and/or a stream of bytes into a Django FileField, with a chosen filename" :)

@rbarrois rbarrois added the Feature label Sep 3, 2014

@ianawilson

This comment has been minimized.

Show comment
Hide comment
@ianawilson

ianawilson Sep 4, 2014

It's been a while since I've looked at this, so I quickly refreshed myself on factory boy and my workaround. Apologies in advance if I'm totally overlooking something.

To refresh myself and make sure I'm being clear, I pulled together some code snippets from my project. My issue was that the data for the FileField was required by my Django model's save() method.

class MyModel(models.Model):
    # truncated example ...

    file = models.FileField()

    def save(self, *args, **kwargs):
        # code that uses self.file.read()
        self.generate_hash()

        super(MyModel, self).save(*args, **kwargs)

The FileField has no value when Factory.create() runs MyModel.save() (because the FileField declaration is a post-generation attribute), so I did the following to my Factory subclass:

class MyFactory(factory.django.DjangoModelFactory):
    # truncated example ...

    FACTORY_FOR = MyModel

    file = factory.django.FileField(data='data', filename='test__can_delete__mymodel')

    @classmethod
    def create(cls, *args, **kwargs):
        """ Workaround for FileField being a post generation attribute """
        instance = cls.build(*args, **kwargs)
        instance.save()
        return instance

Using Factory.build() and then instance.save() forces post-generation tasks to run before the save, which means that my file data is available when instance.save() is called.

Is there a reason that FileField instantiation should be considered post-generation? I don't think that follows the same pattern as the Django's ORM, because I get the behavior I expect when I use MyModel.create() -- I can call self.file.read() before Model's super save() is run. I'm not dying for this feature, since the above workaround above is sufficient, but it seemed odd to me.

EDIT: Python syntax highlighting of examples

ianawilson commented Sep 4, 2014

It's been a while since I've looked at this, so I quickly refreshed myself on factory boy and my workaround. Apologies in advance if I'm totally overlooking something.

To refresh myself and make sure I'm being clear, I pulled together some code snippets from my project. My issue was that the data for the FileField was required by my Django model's save() method.

class MyModel(models.Model):
    # truncated example ...

    file = models.FileField()

    def save(self, *args, **kwargs):
        # code that uses self.file.read()
        self.generate_hash()

        super(MyModel, self).save(*args, **kwargs)

The FileField has no value when Factory.create() runs MyModel.save() (because the FileField declaration is a post-generation attribute), so I did the following to my Factory subclass:

class MyFactory(factory.django.DjangoModelFactory):
    # truncated example ...

    FACTORY_FOR = MyModel

    file = factory.django.FileField(data='data', filename='test__can_delete__mymodel')

    @classmethod
    def create(cls, *args, **kwargs):
        """ Workaround for FileField being a post generation attribute """
        instance = cls.build(*args, **kwargs)
        instance.save()
        return instance

Using Factory.build() and then instance.save() forces post-generation tasks to run before the save, which means that my file data is available when instance.save() is called.

Is there a reason that FileField instantiation should be considered post-generation? I don't think that follows the same pattern as the Django's ORM, because I get the behavior I expect when I use MyModel.create() -- I can call self.file.read() before Model's super save() is run. I'm not dying for this feature, since the above workaround above is sufficient, but it seemed odd to me.

EDIT: Python syntax highlighting of examples

@staab

This comment has been minimized.

Show comment
Hide comment
@staab

staab Nov 18, 2014

I'd be interested in this change as well. The workaround (thanks for that by the way!) could cause problems when you actually do want non-filefield post-generation methods.

staab commented Nov 18, 2014

I'd be interested in this change as well. The workaround (thanks for that by the way!) could cause problems when you actually do want non-filefield post-generation methods.

@rbarrois rbarrois closed this in 4e0e563 Mar 26, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment