So I'm trying to add an extra field to a ModelForm class in the __init__ func, so that on the Create/Update class views I could grab this model and create a new model. Since the Model doesn't have this field, how could I add it in the __init__ func or is there some other way I could add this field?
I've tried overriding __init__ method to include this field in the ModelForm class, but this still throws me an error that the argument is unexpected
class MeterInstallationForm(ModelForm):
class Meta:
model = MeterInstallation
fields = METER_INSTALLATION_DEFAULT_FIELDS
def __init__(self, *args, **kwargs):
instance = kwargs.get("instance")
super(MeterInstallationForm, self).__init__(*args, **kwargs)
if instance:
# get tariff info in the form
# self.fields["tariff"] = instance.tariff
self.fields["tariff"] = TariffApplication.objects.filter(meter_installation__pk=instance.meter_installation.pk).values_list("tariff", flat=True)
class MeterInstallationCreateView(MeterInstallationsViewMixin, LoginRequiredMixin, CreateView):
template_name = "meter_installations/meter_installation_create.html"
fields = (
"name",
"meter_type",
"parent",
"meter",
"building",
"initial_reading",
"final_reading",
"active_after",
"active_until",
"comment",
)
form_class = MeterInstallationForm
meter_installation_create_view = MeterInstallationCreateView.as_view()
class MeterInstallation(ActiveAfterUntilModel, DateTrackedModel, MPTTModel, NamedModel): # type: ignore
meter_type = models.ForeignKey(
MeterType,
on_delete=models.PROTECT,
null=False,
blank=False,
related_name="installations",
verbose_name=_("Meter Installation type"),
)
parent = TreeForeignKey(
"self", on_delete=models.CASCADE, null=True, blank=True, related_name="children", db_index=True
)
meter = models.ForeignKey(
Meter, on_delete=models.PROTECT, related_name="installations", null=False, blank=False, verbose_name=_("Meter")
)
building = models.ForeignKey(
Building,
on_delete=models.PROTECT,
related_name="meter_installations",
null=True,
blank=False,
verbose_name=_("Building"),
)
places = models.ManyToManyField(Place, related_name="meter_installations", blank=False, verbose_name=_("Places"))
initial_reading = models.DecimalField(
decimal_places=4, max_digits=10, null=False, blank=False, default=0, verbose_name=_("Initial reading")
)
final_reading = models.DecimalField(
decimal_places=4, max_digits=10, null=True, blank=True, default=0, verbose_name=_("Final reading")
)
class MPTTMeta:
order_insertion_by = ["meter"]
def get_absolute_url(self):
return reverse("meter-installations:meter-installation-detail", kwargs={"pk": self.pk})
def delete(self, *args, **kwargs):
first_lvl_children = self.get_children().filter(level=1)
for first_lvl_child in first_lvl_children:
first_lvl_child.parent = None
first_lvl_child.save()
for leaf in first_lvl_child.get_children():
leaf.parent = first_lvl_child
leaf.save()
tree_id = first_lvl_child.tree_id
MeterInstallation.objects.partial_rebuild(tree_id)
super(MeterInstallation, self).delete(*args, **kwargs)
def __str__(self):
return f"[{self.pk}] type: {self.meter_type_id}, meter: {self.meter_id}"
class Tariff(ActiveAfterUntilModel, NamedModel, DateTrackedModel):
tariff_type = models.ForeignKey(
MeterType,
on_delete=models.PROTECT,
null=False,
blank=False,
related_name="tariffs",
verbose_name=_("Tariff type"),
)
building = models.ForeignKey(
Building, on_delete=models.PROTECT, related_name="tariffs", null=True, blank=False, verbose_name=_("Building")
)
unit_name = models.CharField(max_length=100, null=False, blank=True, unique=False, verbose_name=_("Unit name"))
unit_price = models.DecimalField(
decimal_places=4, max_digits=10, null=False, blank=False, default=0.0, verbose_name=_("Unit price")
)
VAT = models.DecimalField(decimal_places=2, max_digits=10, null=True, blank=True, verbose_name=_("VAT"))
class Meta:
unique_together = ("name", "tariff_type", "active_after", "active_until")
def get_absolute_url(self):
return reverse("tariffs:tariff-detail", kwargs={"pk": self.pk})
def __str__(self) -> str:
return (
f"[{self.pk}] "
f"type: {self.tariff_type_id}, "
f"building: {self.building_id}, "
f"price: {self.unit_price}, "
f"VAT: {self.VAT}, "
f"active_until: {self.active_until}"
)
class TariffApplication(ActiveAfterUntilModel, DateTrackedModel): # type: ignore
tariff = models.ForeignKey(
Tariff,
on_delete=models.PROTECT,
null=False,
blank=False,
related_name="tariff_applications",
verbose_name=_("Tariff Applications"),
)
meter_installation = models.ForeignKey(
"MeterInstallation",
on_delete=models.PROTECT,
null=False,
blank=False,
related_name="tariff_applications",
verbose_name=_("Meter Installation"),
)
def __str__(self) -> str:
return f"[{self.pk}] tariff: {self.tariff_id}, meter installation: {self.meter_installation_id}"
I would love to know how to make this work, so that in my CreateView I could start creating a third model by the given Tariff
You can use a ModelChoiceField so that you can select a tariff on the form.
class MeterInstallationForm(ModelForm):
class Meta:
model = MeterInstallation
fields = METER_INSTALLATION_DEFAULT_FIELDS
def __init__(self, *args, **kwargs):
super(MeterInstallationForm, self).__init__(*args, **kwargs)
self.fields["tariff"] = forms.ModelChoiceField(queryset=Tariff.objects.all())
Then in your form_valid() method, you can retrieve the tariff from cleaned_data and create the related TariffApplication.
def form_valid(self, form):
instance = form.save()
TariffApplication.objects.create(tariff=form.cleaned_data['tariff'], meter_installation=instance)
return HttpResponseRedirect(self.get_success_url())
You may need to change the queryset if you need to filter the list of available tariffs. In your original question, I don't think it makes sense to have if instance in the form's __init__ method, because an instance won't be passed to the form for a CreateView.
You can include extra field in your ModelForm and set that in your view such as:
# forms.py
class MeterInstallationForm(ModelForm):
class Meta:
model = MeterInstallation
fields = METER_INSTALLATION_DEFAULT_FIELDS + ('tariff',)
# views.py
class MeterInstallationCreateView(MeterInstallationsViewMixin, LoginRequiredMixin, CreateView):
template_name = "meter_installations/meter_installation_create.html"
fields = (
"name",
"meter_type",
"parent",
"meter",
"building",
"initial_reading",
"final_reading",
"active_after",
"active_until",
"comment",
)
form_class = MeterInstallationForm
def form_valid(self, form):
form.instance.tariff = forms.TariffApplication(form.data)
return super().form_valid(form)
Related
is it possible to add an age field that is auto filled in the runtime based on another date of birth field at the django admin interface, i added a screenshot trying to explain more what i mean
my models.py
class FamilyMember(models.Model):
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE)
family_group = models.ForeignKey(FamilyGroup,
on_delete=models.CASCADE,
null=True,
blank=True)
name = models.CharField(max_length=100, null=True, blank=True)
date_of_birth = models.DateField(null=True, blank=True)
relationship = models.ForeignKey(Relationship, on_delete=models.PROTECT)
dependant_child_age_range = models.ForeignKey(DependantChildAgeRange,
null=True,
blank=True,
on_delete=models.PROTECT)
care_percentage = models.PositiveSmallIntegerField(
null=True, blank=True, validators=[
MaxValueValidator(100),
])
income = models.DecimalField(max_digits=6,
decimal_places=2,
null=True,
blank=True)
rent_percentage = models.PositiveSmallIntegerField(
null=True, blank=True, validators=[
MaxValueValidator(100),
])
admin.py
class FamilyMemberInline(admin.TabularInline):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
action = request.META['PATH_INFO'].strip('/').split('/')[-1]
if action == 'change':
transaction_id = request.META['PATH_INFO'].strip('/').split('/')[-2]
if db_field.name == "family_group":
kwargs["queryset"] = FamilyGroup.objects.filter(transaction=transaction_id)
return super(FamilyMemberInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
model = FamilyMember
extra = 0
def sufficient_info_provided (self, obj):
return obj.sufficient_information_provided
sufficient_info_provided.boolean = True
readonly_fields = ['sufficient_info_provided',]
Override your inline's get_queryset method to annotate the queryset with the calculation. The annotation will add an age attribute to each object in the queryset.
Then as you can see in the ModelAdmin.list_display documentation, you can include a string representing a ModelAdmin method that accepts one argument, the model instance. Inline's work in the same way but you must include the method in ModelAdmin.readonly_fields.
Putting it all together:
class FamilyMemberInline(admin.TabularInline):
...
fields = (..., 'get_age')
readonly_fields = ('get_age',)
def get_queryset(self, request):
return (
super().get_queryset(request)
.annotate(age=...)
)
def get_age(self, instance):
return instance.age
I have a created Django-CMS Plugin with a ManytoManyField to another model. When creating the Plugin on the Front-End, i want the user to be able to filter the ManytoManyField list (which might be too long).
By the way this future is already on Django admin:
class ModelAdmin(admin.ModelAdmin):
list_filter = ('field_name', )
form = PartnerLogoTableForm
Is it possible to have something similar like that on my
cms_plugins.py:
class PartnerLogoTablePlugin(CMSPluginBase):
model = LogoTablePlugin
form = LogoTablePluginForm
name = _('LogoTable Plugin')
render_template = False
search_fields = ('description',)
def render(self, context, instance, placeholder):
self.render_template = 'aldryn_logo_tables/plugins/%s/logotable.html' % instance.style
context.update({
'object': instance,
'placeholder': placeholder,
})
return context
plugin_pool.register_plugin(PartnerLogoTablePlugin)
models.py:
class PartnerLogoTable(models.Model):
name = models.CharField(_('Partner name'), max_length=255)
image = FilerImageField(verbose_name=_('Image'), null=True, blank=True, on_delete=models.SET_NULL)
partner_url = models.TextField(_('Partner url'), null=True, blank=True, validators=[URLValidator()])
is_active = models.BooleanField(_('Is active'), blank=True, default=True)
created_at = models.DateTimeField(_('Created at'), auto_now_add=True)
updated_at = models.DateTimeField(_('Updated at'), auto_now=True)
order = models.IntegerField(_('Order'), null=True, blank=True)
class Meta:
verbose_name = _('Partner Logo')
verbose_name_plural = _('Partner Logos')
ordering = ['order']
def __str__(self):
return self.name
class LogoTablePlugin(CMSPlugin):
DEFAULT = 'default'
LOGOTABLE_CHOICES = [
(DEFAULT, _('Default')),
]
description = models.CharField(_('Description'), max_length=255, null=True, blank=True)
partner = models.ManyToManyField(PartnerLogoTable, verbose_name=_('Partner'))
logo_per_row = models.IntegerField(_('Logo per line'), default=1, null=True, blank=True,
validators=[MaxValueValidator(4), MinValueValidator(1)],
help_text=_('Number of logos to be displayed per row'))
style = models.CharField(_('Style'), choices=LOGOTABLE_CHOICES + get_additional_styles(), default=DEFAULT,
max_length=50, blank=True, null=True, )
class Meta:
verbose_name = _('Partner Logo Plugin')
verbose_name_plural = _('Partner Logo Plugins')
def __unicode__(self):
return u'%s' % self.description
forms.py
class LogoTablePluginForm(forms.ModelForm):
model = LogoTablePlugin
def clean_style(self):
.....
return style
class PartnerLogoTableForm(forms.ModelForm):
model = PartnerLogoTable
def clean(self):
....
return self.cleaned_data
This is how the plugin looks now
ModelSelect2Multiple from django-autocomplete-light seems perfect for your use case.
I am trying to build this feedback application for the students to give feedback to their teachers. I am getting the following error when I try to create an instance of the feedback form. Please help. Thanks.
Error:
ValueError at /feedback/by/XXX/new
Cannot assign "<SimpleLazyObject: <User: XXX>>": "Feedback.user" must be a "User" instance.
CreateView:
class CreateFeedback(LoginRequiredMixin, SelectRelatedMixin, generic.CreateView):
form_class = FeedbackForm
model = models.Feedback
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super().form_valid(form)
Form:
class FeedbackForm(forms.ModelForm):
class Meta:
fields = ("teacher", "subject", "param1", "param2", "param3", "param4",
"param5", "param6", "param7", "param8", "param9", "param10")
model = models.Feedback
Path in urls.py:
path('by/<username>/new',views.CreateFeedback.as_view(),name='create'),
Models.py:
class Feedback(models.Model):
user = models.ForeignKey(User, related_name = 'feedback', on_delete=models.CASCADE, blank=False, null=False)
teacher = models.ForeignKey(Teacher, related_name = 'feedback', on_delete=models.CASCADE, blank=False, null=False)
subject = models.ForeignKey(Subject, related_name = 'feedback', on_delete=models.CASCADE, blank=False, null=False)
param1 = models.PositiveIntegerField(blank=False, null=False)
param2 = models.PositiveIntegerField(blank=False, null=False)
param3 = models.PositiveIntegerField(blank=False, null=False)
param4 = models.PositiveIntegerField(blank=False, null=False)
param5 = models.PositiveIntegerField(blank=False, null=False)
param6 = models.PositiveIntegerField(blank=False, null=False)
param7 = models.PositiveIntegerField(blank=False, null=False)
param8 = models.PositiveIntegerField(blank=False, null=False)
param9 = models.PositiveIntegerField(blank=False, null=False)
param10 = models.PositiveIntegerField(blank=False, null=False)
def get_absolute_url(self):
return reverse(
"feedback:detail",
kwargs={"username": self.user.username, "pk": self.pk})
def __str__(self):
return (self.username + "->" + self.teacher)
class Meta:
ordering = ["teacher"]
unique_together = ["user", "teacher", "subject"]
class User(auth.models.User, auth.models.PermissionsMixin):
def __str__(self):
return self.username
Earlier Django was allowing an integer to assign to a user object, but don't know since when, but it needs a user object only.
To fix your issue
Replace the following line
self.object.user = self.request.user
with following one
self.object.user = User.objects.get(id=self.request.user.id)
I have this simple model and I am having difficult in inserting related field of 'notes' through django rest framework.
class Student(models.Model):
firstName = models.CharField(max_length=100, blank=True, null=True)
lastName = models.CharField(max_length=100, blank=True, null=True)
prefLocation = models.ManyToManyField("Location", blank=True, null=True, related_name = 'prefLocation')
def __unicode__(self):
return self.firstName
class LocationRegion(models.Model):
regionName = models.CharField(max_length=100)
def __unicode__(self):
return self.regionName
class Location(models.Model):
locationName = models.CharField(max_length=100)
region = models.ForeignKey(LocationRegion, null=True, blank=True, related_name='locations')
def __unicode__(self):
return self.locationName
class Note(models.Model):
text = models.CharField(max_length=1000)
student = models.ForeignKey(Student, null=True, blank=True, related_name='notes')
def __unicode__(self):
return self.text
class StudentViewSet(viewsets.ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentSerializer
I am unsure if I need to use ModelSerializer or generic Serializer. Validated_data doesn't return 'note' field in the deserialized data. I would appreciate any help with the serializer.
Thanks
Here are my serializers :
class StudentSerializer(serializers.ModelSerializer):
def create(self, validated_data):
def get_notes(self, obj):
return validated_data['note']
note = serializers.SerializerMethodField('get_notes')
return Candidate.objects.create(**validated_data)
class Meta:
model = Student
fields = ('id', 'firstName', 'lastName', 'note')
class NoteSerializer(serializers.ModelSerializer):
class Meta:
model = Note
I have 2 models, Question and Image. I want to create a manager in django admin, to include the fields of Image inside the Question admin panel.
These are the models:
class Question(models.Model):
quiz = models.ManyToManyField(Quiz, blank=True, )
category = models.ForeignKey(Category, blank=True, null=True, )
content = models.CharField(max_length=1000,
blank=False,
help_text="Enter the question text that you want displayed",
verbose_name='Question',
)
explanation = models.TextField(max_length=2000,
blank=True,
help_text="Explanation to be shown after the question has been answered.",
verbose_name='Explanation',
)
class Meta:
verbose_name = "Question"
verbose_name_plural = "Questions"
ordering = ['category']
def __unicode__(self):
return self.content
class Image(models.Model):
TYPE_CHOICES = (
('A','Answer'),
('Q','Question'),
)
image = models.ImageField(upload_to='static/img')
type = models.CharField(max_length=1, choices=TYPE_CHOICES)
question = models.ForeignKey(Question, blank=True, null=True)
answer = models.ForeignKey(Answer, blank=True, null=True)
def __unicode__(self):
return self.type
This is the Question Manager in Django Admin:
class QuizAdminForm(forms.ModelForm):
class Meta:
model = Quiz
questions = forms.ModelMultipleChoiceField(
queryset=Question.objects.all(),
required=False,
widget=FilteredSelectMultiple(verbose_name=('Questions'),
is_stacked=False )
)
def __init__(self, *args, **kwargs):
super(QuizAdminForm, self).__init__(*args, **kwargs)
if self.instance.pk:
self.fields['questions'].initial = self.instance.question_set.all()
def save(self, commit=True):
quiz = super(QuizAdminForm, self).save(commit=False)
if commit:
quiz.save()
if quiz.pk:
quiz.question_set = self.cleaned_data['questions']
self.save_m2m()
return quiz
You are looking InlineModelAdmin models.
class ImageInline(admin.TabularInline):
model = Image
...
class QuestionAdmin(admin.ModelAdmin):
list_display = ('content', 'category', )
list_filter = ('category',)
fields = ('content', 'category', 'quiz', 'explanation')
search_fields = ('content', 'explanation')
filter_horizontal = ('quiz',)
inlines = [AnswerInline, ImageInline]
https://docs.djangoproject.com/en/1.6/ref/contrib/admin/#inlinemodeladmin-objects
Good to see you are using Django Quiz app. I have recently added a lot of changes to it and it would be good if you could contribute anything to the repo:
https://github.com/tomwalker/django_quiz