I want to overwrite a creation of M2M through model object. I thought that overwriting save method will be sufficient, but it appears that after saving admin form, the method is not called. I am having a hard time finding how this object is created.
Here is the code snippet
class ProductVariantToAttributeValue(models.Model):
product_variant = models.ForeignKey(ProductVariant, on_delete=models.CASCADE)
attribute_value = models.ForeignKey(AttributeValue, on_delete=models.CASCADE)
attribute = models.ForeignKey(
Attribute, on_delete=models.CASCADE, null=True, blank=True
)
class Meta:
db_table = "productvariants_to_attributevalues"
unique_together = [("product_variant", "attribute")]
verbose_name_plural = "Product Variants To Attribute Values"
def save(self, **kwargs):
self.attribute = self.attribute_value.attribute
super().save(**kwargs)
Related
Full Error
Got AttributeError when attempting to get a value for field segments on serializer BackboneLineSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the RelatedManager instance.
Original exception text was: 'RelatedManager' object has no attribute 'segments'.
I can't figure out why I'm getting this error. I'm using what I think is the exact same code elsewhere with it working. The only thing I can think of is I may have changed the related_name on the backbone_line field of the BackboneLineSegments model from something else to 'segments'. All migrations have been applied, and I don't know why it would be causing issues. Maybe the database doesn't update the new related_name correctly? I'm using Postgres in case it matters.
Models
class BackboneLine(models.Model):
sub_area = models.ForeignKey(
SubArea,
on_delete=models.SET_NULL,
null=True,
related_name='backbone_lines')
source = models.ForeignKey(
Enclosure,
on_delete=models.SET_NULL,
null=True,
related_name='backbone_lines_out'
)
destination = models.OneToOneField(
Enclosure,
on_delete=models.SET_NULL,
null=True,
related_name='backbone_line_in'
)
#property
def label(self):
return f'{self.source.box_number} - {self.destination.box_number}'
class BackboneLineSegment(models.Model):
location = models.LineStringField()
buried = models.BooleanField(default=False)
backbone_line = models.ForeignKey(
BackboneLine,
on_delete=models.CASCADE,
related_name='segments'
)
#property
def long_lat(self):
return [list(coord_pair) for coord_pair in self.location]
#property
def lat_long(self):
return [(cords[1], cords[0]) for cords in self.location.coords]
class SubArea(models.Model):
label = models.CharField(max_length=20)
area = models.ForeignKey(
Area, on_delete=models.SET_NULL, null=True, related_name='sub_areas')
fiber_switch = models.ForeignKey(
'FiberSwitch', on_delete=models.SET_NULL, null=True, related_name='sub_areas'
)
def __str__(self):
return self.label
Serializers
class BackboneLineSegmentSerializer(GeoFeatureModelSerializer):
class Meta:
model = BackboneLineSegment
geo_field = 'location'
fields = ['location', 'buried']
class BackboneLineSerializer(ModelSerializer):
segments = BackboneLineSegmentSerializer(many=True)
class Meta:
model = BackboneLine
fields = ['label', 'segments']
class SubAreaSerializer(ModelSerializer):
enclosures = EnclosureSerializer(many=True)
backbone_lines = BackboneLineSerializer(many=True)
class Meta:
model = SubArea
fields = ['label', 'enclosures', 'backbone_lines']
Code Throwing Error
sub_area = SubArea.ocbjects.get(pk=1)
sub_area_serialier = SubAreaSerializer(sub_area)
So I have a model like this
class DataSheet(BaseModel):
"""
Represents a single dataSheet.
dataSheets have their own model at the core. Model data is added to
the dataSheets in the form of separate records.
"""
class Meta:
verbose_name = 'datasheet'
verbose_name_plural = 'datasheets'
ordering = ['position', 'cluster']
required_db_features = {
'supports_deferrable_unique_constraints',
}
constraints = [
models.UniqueConstraint(
fields=['position', 'cluster'],
name='deferrable_unique_datasheet_position',
deferrable=models.Deferrable.DEFERRED
)
]
def __str__(self):
return self.name
objects = managers.DataSheetsManager()
positions = managers.PositionalManager()
position = models.PositiveSmallIntegerField(db_index=True, editable=True)
name = models.CharField(max_length=100, validators=[MinLengthValidator(2)], db_index=True)
description = models.CharField(max_length=1024, null=True, blank=True, db_index=True)
owner = models.ForeignKey('api_backend.Member', on_delete=models.CASCADE, db_index=True, editable=False)
fields = models.ManyToManyField('api_backend.Field')
overwrites = models.ManyToManyField('api_backend.RoleOverwrite')
parent = models.ForeignKey('api_backend.Category', on_delete=models.CASCADE, null=True, blank=True)
cluster = models.ForeignKey('api_backend.Cluster', on_delete=models.CASCADE, editable=False)
REQUIRED_FIELDS = [name, owner, cluster]
and a serializer like this
class DataSheetSerializer(serializers.ModelSerializer):
"""
A serialized DataSheet Object.
Datasheets have their own:
- array of fields
- array of role-overwrites
"""
def get_fields(self):
fields = super(DataSheetSerializer, self).get_fields()
fields['parent'].queryset = self.cluster.categories.all()
return fields
class Meta:
model = DataSheet
read_only_fields = ['position']
fields = '__all__'
# need to make sure that the parent category of the datasheet
# belongs to the datasheet's cluster only.
fields = partial.PartialFieldSerializer(many=True, read_only=True)
overwrites = partial.PartialOverWriteSerializer(many=True, read_only=True)
the thing is, I want to access the serializer model's cluster field inside of the get_fields method. However, I couldn't do the same. Can someone help me?
I've seen other answers involving initial_data, but that doesn't work here.
fields['parent'].queryset = self.cluster.categories.all()
cluster is an unresolved reference here.
self in get_fields is DataSheetSerializer instance not DataSheet model instance. hence it should not have cluster property. you can not access model DataSheet instance in get_fields as it gets fields from class DataSheet not from its instance. you can validate the field like
class DataSheetSerializer(serializers.ModelSerializer):
# ... other code
def validate(self, data):
parent = data.get('parent')
# check if parent is valid i.e in queryset
# if yes return data
# else raise serializers.validationError
Hi I have the following django model:
class Issue(models.Model):
title = models.CharField(max_length=200)
date = models.DateTimeField(auto_now=True)
assignee = models.ForeignKey(User, on_delete=models.CASCADE, related_name='assignee')
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='owner', null=True, blank=True)
description = models.TextField()
state = models.IntegerField(choices=STATUS_CHOICES, default=1)
priority = models.IntegerField(choices=RELEVANCE_CHOICES, default=2)
expired_date = models.DateField(auto_now=False, null=True, blank=True)
and a form which allow a user to create an Issue instance:
class IssueForm(forms.ModelForm):
class Meta:
model = Issue
fields = ('title', 'description', 'assignee', 'state', 'priority', 'expired_date')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'].label = "Titolo"
self.fields['description'].label = "Descrizione"
self.fields['state'].label = "Stato"
self.fields['priority'].label = "Priorità"
self.fields['expired_date'].label = "Termine"
self.fields['expired_date'].widget.attrs.update({'class': 'datepicker'})
self.fields['assignee'] = forms.MultipleChoiceField(
choices=self.fields['assignee'].choices,
widget=forms.CheckboxSelectMultiple,
label=("Assegnatario")
)
def clean(self):
cleaned_data = super().clean()
user_id = [i for i in cleaned_data['assignee']]
cleaned_data['assignee'] = [User.objects.get(id=i) for i in user_id]
return cleaned_data
I render this form and the field assignee is a checkbox.
I would like to be able to choose several assignee for the same issue, but I got an error because the Issue model expect just one User instance
How can I modify my model Issue in order to get more than one user ?
Thanks
you can create a new class and name it Issue_Instance where every Issue Object can have an assignee as a foreign key the problem that the relation is one to many because you have to choose more than one assignee and Django doesn't support the idea of having Array or List of Foreign Keys(I don't know any frame works that do :=) ) so I would suggest creating a new class or make the foreign key relation one-to-many key field read about it it will be very useful to solve your problem
Looking for solution of this problem I encountered some similar threads, but referring to older versions of Django/DRF and thus not working in my case.
There are these two models:
class CsdModel(models.Model):
model_id = models.CharField("Item ID", max_length=8, primary_key=True)
name = models.CharField("Item Name", max_length=40)
active = models.BooleanField(default=True)
def __str__(self):
return self.model_id
class CsdListing(models.Model):
model_id = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='m_id')
name = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='m_name')
(...)
EDIT: Serializers are defined this way:
class CsdModelSerializer(serializers.ModelSerializer):
model_id = serializers.RegexField(regex='^\w{2}\d{3}$', allow_blank=False)
name = serializers.CharField(min_length=6, max_length=50, allow_blank=False)
class Meta:
model = CsdModel
fields = '__all__'
class CsdListingSerializer(serializers.ModelSerializer):
session_id = serializers.RegexField(regex='^s\d{2}$', allow_blank=False)
def validate_session_id(self, value):
(...)
class Meta:
model = CsdListing
fields = '__all__'
What I'd like to see, is model_id and name from CsdModel displayed inside a form created based on CsdListing model. But instead, the ID is duplicated:
How should I rebuild the model(s) to have both ID and name displayed in the form?
You should have only one foreign key. But the listing serializer should then reference the model as a nested serializer.
class CsdListing(models.Model):
model = models.ForeignKey(CsdModel, on_delete=models.CASCADE, default=0, related_name='listing')
class CsdListingSerializer(serializers.ModelSerializer):
model = CsdModelSerializer()
session_id = serializers.RegexField(regex='^s\d{2}$', allow_blank=False)
I the following in the models.py:
class Item(models.Model):
date = models.DateField(_('date'), blank=True, null=True)
description = models.CharField(_('description'), max_length=255)
content_type = models.ForeignKey(ContentType, verbose_name=_('content type'))
object_id = models.PositiveIntegerField(_('object id'), db_index=True)
object = generic.GenericForeignKey('content_type', 'object_id')
class ItemAccountAmountRef(Item):
""" Items of which a Quote or an Invoice exists. """
amount = models.DecimalField(max_digits=10, decimal_places=2)
reference = models.CharField(max_length=200)
debit_account = models.ForeignKey(Account, related_name='receivables_receipt_debit_account')
credit_account = models.ForeignKey(Account, related_name='receivables_receipt_credit_account')
class PaymentItem(ItemAccountAmountRef):
pass
class Payment(models.Model):
invoice = models.ManyToManyField(Invoice, null=True, blank=True)
date = models.DateField('date')
attachments = generic.GenericRelation(Attachment)
site = models.ForeignKey(Site, related_name='payment_site', null=True, blank=True
items = generic.GenericRelation(PaymentItem)
in the admin.py:
class PaymentItemInline(generic.GenericTabularInline):
model = PaymentItem
form = PaymentItemForm
class PaymentAdmin(admin.ModelAdmin):
inlines = [PaymentItemInline]
in forms.py:
class PaymentItemForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PaymentItemForm, self).__init__(*args, **kwargs)
self.fields['credit_account'].label = "Bank Account"
In the PaymentItemInline the label is not changing. I have tried changing other attributes e.g. class which work. If I run through the init in debug mode I can see that the label variable is changing however when the form is rendered the field is still labelled credit account. Any suggestions?
You're 98% of the way there. Instead of trying to futz with the form field in __init__, just redefine it in your ModelForm. If you name it the same thing, django will be able to figure out that it is supposed to validate & save to the ForeignKey field. You can use the same formula to change a Field or Widget completely for a given field in a ModelForm.
You can find the default form field types for each model field type here: https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#field-types
class PaymentItemForm(forms.ModelForm):
credit_account = forms.ModelChoiceField(label="Bank Account", queryset=Account.objects.all())
That's it. No need to override any functions at all : )
Incidentally, the docs for this field are here: https://docs.djangoproject.com/en/dev/ref/forms/fields/#modelchoicefield