2

I'm trying to change my model.

For now, I use other model to save images for my posts.

I think most people use like this.

However, I heard django supports ArrayField if database is postgresql.

import django.contrib.postgres.fields import ArrayField

so, I trying to change my model. such as

class post(models.Model):
    title = ...
    content = ...
    images = ArrayField(base_field=models.ImageField(...))

What I want to know is How can I create these base_field's data in view? we can access normally if model is

class tmp(models.Model):
    img = models.ImageField()

in view, or wherever, we just do like

img = request.FILES['data']
tmp.save()

this.

However, In ArrayField, I want to do is

tmp_imgs = TemporaryImagesTable.objects.filter(user=user)
for tmp_img in tmp_imgs:
    images.append(???????????????) (append or images = ?????)

I want to do is

images.append(new ImageField(tmp_img))

like this!

please help me,

4
  • This is a classic case of breaking something by trying to fix something that's not broken. Commented Aug 10, 2016 at 11:35
  • hmm, of course I agree with that. Just I try to reduce one table for images. Also the reason i want to know this is that what if i face this problem can not avoid after. Commented Aug 10, 2016 at 11:45
  • that's not what arrayfield was designed for. I recommend reading the postgresql arrays documentaton Commented Aug 10, 2016 at 11:46
  • Oh, i saw it can be anything that subclass of field. If not, when arrayfield can use? Give some examples, I appreciate that. Commented Aug 10, 2016 at 12:10

2 Answers 2

8

I had a similar implementation just like yours and ended up using without the array.

No doubt that ArrayField supported by PostgreSQL is very tempting at time's that clouds your judgement about how you want to use it.

ArrayField is best used to store data like Tags for a Post or a Product. Which could be easy implemented if you are searching for a particular tag or just filtering.

But ImageFields are not searchable or to put in a better way its not optimized for such operations. I ended up using two table for the problem:

class Post(models.Model):
    author = # Character
    place = # String
    comment = # TextField

class PostImages(models.Model):
    post  = models.ForeignKey(Post)
    image = models.ImageField()# All the meta data requried for the field

In views.py, to get all the images for a particular post :

# To save the post and Images
def save_images(request):
    post_data = # get all the data for post object,
                # either from form or as individual data    
    images = # Get images data from the request

    # Using atomic transactions will save from writing any data to the 
    # Database unless all the operations have been performed without any error
    with transaction.atomic():
        # Save the post data into a Post object in any manner
        post_obj = Post.objects.save(**post_data)
        # Saving all the multiple images 
        for each_image in images:
            image_obj = PostImages()
            image_obj.post = post_obj
            image_obj.image = each_image
            image_obj.save()
        # Return success or do something here  


 # To search or fetch images for post
 def get_post_images(post_pk):
      post_obj = Post.objects.get(pk=post_pk)
      post_images = PostImages(post=post_obj)
      # Do something for both of them

Here you can find more about transaction.atomic() if you are interested to know more about it.

Like always this is one of the way to do it, there must be other ways to do it according to your specification

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your help dude!
No problem, glad it helped
0

I was able to get it working, however I don't recommend doing this because you lose the following benefits of having an image table with a foreign key reference to the post:

  • Unless this list is readonly, you can't make atomic edits to individual images. (e.g. what happens when we both try to delete the last image)
  • Using an ImageField with Django's storage integration is helpful when you're using a common cloud provider so that its actually deleting the image in cloud storage

Here is the following code:

import uuid
import os

def generate_image_filename(instance, filename):
    _, extension = os.path.splitext(filename)
    filename = f'{instance._meta.model_name}/{uuid.uuid4()}{extension}'
    return filename

def get_array_of_urls_from_arrayfield(obj, field_name='images'):
    storage = obj._meta.get_field(field_name).base_field.storage
    return [storage.url(image) for image in getattr(obj, field_name)]

class ImageStorage(GoogleCloudStorage):
    bucket_name = 'bucket'

class Post(models.Model):
    images = ArrayField(
             base_field=models.ImageField(
                   upload_to=generate_image_filename, 
                   storage=ImageStorage()
             )

class PostSerializer(serializers.ModelSerializer):
    images = serializers.SerializerMethodField()

    def get_images(self, obj):
        return get_array_of_urls_from_arrayfield(obj)

My views.py code uses the GenericViewSets with standard CRUD mixins.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.