[docs]classArrayField(Field):empty_strings_allowed=Falsedefault_error_messages={'item_invalid':_('Item %(nth)s in the array did not validate: '),'nested_array_mismatch':_('Nested arrays must have the same length.'),}def__init__(self,base_field,size=None,**kwargs):self.base_field=base_fieldself.size=sizeifself.size:self.default_validators=self.default_validators[:]self.default_validators.append(ArrayMaxLengthValidator(self.size))# For performance, only add a from_db_value() method if the base field# implements it.ifhasattr(self.base_field,'from_db_value'):self.from_db_value=self._from_db_valuesuper().__init__(**kwargs)@propertydefmodel(self):try:returnself.__dict__['model']exceptKeyError:raiseAttributeError("'%s' object has no attribute 'model'"%self.__class__.__name__)@model.setterdefmodel(self,model):self.__dict__['model']=modelself.base_field.model=modeldefcheck(self,**kwargs):errors=super().check(**kwargs)ifself.base_field.remote_field:errors.append(checks.Error('Base field for array cannot be a related field.',obj=self,id='postgres.E002'))else:# Remove the field name checks as they are not needed here.base_errors=self.base_field.check()ifbase_errors:messages='\n '.join('%s (%s)'%(error.msg,error.id)forerrorinbase_errors)errors.append(checks.Error('Base field for array has errors:\n%s'%messages,obj=self,id='postgres.E001'))returnerrorsdefset_attributes_from_name(self,name):super().set_attributes_from_name(name)self.base_field.set_attributes_from_name(name)@propertydefdescription(self):return'Array of %s'%self.base_field.descriptiondefdb_type(self,connection):size=self.sizeor''return'%s[%s]'%(self.base_field.db_type(connection),size)defget_db_prep_value(self,value,connection,prepared=False):ifisinstance(value,(list,tuple)):return[self.base_field.get_db_prep_value(i,connection,prepared=False)foriinvalue]returnvaluedefdeconstruct(self):name,path,args,kwargs=super().deconstruct()ifpath=='django.contrib.postgres.fields.array.ArrayField':path='django.contrib.postgres.fields.ArrayField'kwargs.update({'base_field':self.base_field.clone(),'size':self.size,})returnname,path,args,kwargsdefto_python(self,value):ifisinstance(value,str):# Assume we're deserializingvals=json.loads(value)value=[self.base_field.to_python(val)forvalinvals]returnvaluedef_from_db_value(self,value,expression,connection):ifvalueisNone:returnvaluereturn[self.base_field.from_db_value(item,expression,connection,{})iffunc_supports_parameter(self.base_field.from_db_value,'context')# RemovedInDjango30Warningelseself.base_field.from_db_value(item,expression,connection)foriteminvalue]defvalue_to_string(self,obj):values=[]vals=self.value_from_object(obj)base_field=self.base_fieldforvalinvals:ifvalisNone:values.append(None)else:obj=AttributeSetter(base_field.attname,val)values.append(base_field.value_to_string(obj))returnjson.dumps(values)defget_transform(self,name):transform=super().get_transform(name)iftransform:returntransformif'_'notinname:try:index=int(name)exceptValueError:passelse:index+=1# postgres uses 1-indexingreturnIndexTransformFactory(index,self.base_field)try:start,end=name.split('_')start=int(start)+1end=int(end)# don't add one here because postgres slices are weirdexceptValueError:passelse:returnSliceTransformFactory(start,end)defvalidate(self,value,model_instance):super().validate(value,model_instance)forindex,partinenumerate(value):try:self.base_field.validate(part,model_instance)exceptexceptions.ValidationErroraserror:raiseprefix_validation_error(error,prefix=self.error_messages['item_invalid'],code='item_invalid',params={'nth':index},)ifisinstance(self.base_field,ArrayField):iflen({len(i)foriinvalue})>1:raiseexceptions.ValidationError(self.error_messages['nested_array_mismatch'],code='nested_array_mismatch',)defrun_validators(self,value):super().run_validators(value)forindex,partinenumerate(value):try:self.base_field.run_validators(part)exceptexceptions.ValidationErroraserror:raiseprefix_validation_error(error,prefix=self.error_messages['item_invalid'],code='item_invalid',params={'nth':index},)defformfield(self,**kwargs):defaults={'form_class':SimpleArrayField,'base_field':self.base_field.formfield(),'max_length':self.size,}defaults.update(kwargs)