Django create new instances of a different model using the submitted form data of a model

The name of the pictureThe name of the pictureThe name of the pictureClash 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?





Form and View are different. Which one is using here?
– JPG
Aug 5 at 3:20





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.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard