Django create new instances of a different model using the submitted form data of a model
Clash Royale CLAN TAG#URR8PPP
Django create new instances of a different model using the submitted form data of a model
I have a Topic model which has a ManyToManyField to the Tag model.
Similarly to stack overflow where you can submit new tags at the time of asking a question. I want to be able to create new tags when you create a topic.
A hacky solution from the top of my head.
Class TopicCreateView(CreateView):
model = Topic
template_name = 'blah'
fields = [
'blah'
]
def form_valid(self, form):
tags = self.request.POST.getlist(tags)
for tag in tags:
if not Tag.objects.filter(tag_string__iexact=tag):
try:
new_tag = Tag.objects.create(tag_string=tag)
except IntegrityError:
# stuff
return super().form_valid(form)
I'm new to Django and web frameworks in general, but this seems really hacky to me (if it even works). Given what I've read so far regarding FormSets and such, is there not a better way to achieve this?
Sorry, Should have made it clear, I'm using the built in CreateView, without a form_class, specifying the model. Updated the example to reflect.
– STiGYFishh
Aug 5 at 3:24
did you try your solution? Any problem?
– JPG
Aug 5 at 3:41
No I haven't tried it yet. I have other dependencies that need implementing first, before this View will work. But even if it does, my question still stands. Is there a better way?
– STiGYFishh
Aug 5 at 4:02
can you show your Topic model as well as Tag model?
– JPG
Aug 5 at 4:36
2 Answers
2
Since the model code is not provided, I'm just guessing what you are thinking about:
def form_valid(self, form):
tags = self.request.POST.getlist('tags')
existed_tags = Tag.objects
.filter(tag_string__iexact=tags)
.values_list('tag_string', flat=True)
new_tags = set(tags) - set(existed_tags)
for tag in new_tags:
Tag.objects.create(tag_string=tag)
return super().form_valid(form)
Perhaps your original codes can work but just need improvement (and thus I don't think it was 'hacky'). You put the Tag
QuerySet within a for loop so the database will be hit in each time of iteration. To get all of the tag values from db first then compare the difference and do further work may be a better way.
Tag
Moreover, I think tag creation should not put in form_valid
since this is a 'model procedure'. To override the save()
method of Topic
model or using Signal may be a better choice. Yet this is just my preference and you can still keep this unchanged.
form_valid
save()
Topic
This is what my thoughts were yes, this should not be in form_valid, but I assume I cannot define a save method without creating a form class? How would I use signals to achieve this? Can I use the pre_save() as a decorater perhaps? The only issue i see with this is, if there is an error how do I invalidate the form and feed it back to the user, as at the save stage the form has already been validated no?
– STiGYFishh
Aug 5 at 4:32
Sorry, maybe I make an over-assumption here. I just think it shall trigger
Topic.save()
after sending the form since you use a CreateView
with model=Topic
. then your can override the save()
method or trigger 'pre_save` signal via Topic
saving. If that is not the case, please kindly ignore them.– Tsang-Yi Shen
Aug 5 at 4:57
Topic.save()
CreateView
model=Topic
save()
Topic
I came up with a more acceptable solution keeping the model creation out of the View()
and in the Form()
, as defining form_class
is still possible with the CreateView()
.
I took inspiration from how stack overflow handles tags, in that they accept a string and split each tag on '+'
.
View()
Form()
form_class
CreateView()
'+'
There may be a way to further improve this but FormSets
did not seem like viable option in this instance.
FormSets
Class TopicCreateForm(forms.ModelForm)
submitted_tags = forms.CharField(max_length=256)
class Meta:
model = Blah
fields = ['blah']
def clean_submitted_tags(self):
tags = self.cleaned_data.get(
'submitted_tags').split("+")
# validation here
return tags
def save(self, *args, **kwargs):
# get author from form instance
author = self.instance.author
tags = self.cleaned_data['submitted_tags']
existing_tags = Tag.objects.values_list(
'tag_string', flat=True)
for tag in [t for t in tags if t not in existing_tags]:
new_tag = Tag.objects.create(
tag_string=tag,
created_by=author)
new_tags = Tag.objects.filter(tag_string__in=tags)
new_tag_uuids = [t.pk for t in new_tags]
self.cleaned_data['tags'] = new_tag_uuids
return super().save(*args, **kwargs)
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Form and View are different. Which one is using here?
– JPG
Aug 5 at 3:20