This maybe a simple question, but I do not have any idea how to do this.
I have two models in Django.
class ModelA(models.Model):
some_member = models.Charfield(...)
class ModelB(models.Model):
model = OneToOneField(ModelA)
other_member = models.Charfield(...)
ModelA is filled in by a ModelForm and then redirect to the form for ModelB.
How can I Autofill the OneToOneField based on the previous form.
Thank you for helping.
I am doing it like this now
class ModelBView(CreateView):
.......
def form_valid(self, form):
model_b = form.save(commit=False)
model_a_form = ModelAForm(self.request.POST)
model_b.model = model_a_form.save(commit=False)
model_b.save()
return super(ModelBView, self).form_valid(form)
but get the error: "...could not be created because the data didn't validate."
So here the problem is, I simply can get the data from the previous form by request.POST.
How can I get this data.
Maybe you can save the data from ModelForm for ModelA in request.session or request.get and redirect to the next page for ModelB, where you can get the data for model field in ModelB then fill it.
It depends on what you want exactly.
If you want to auto-fill the field in the second form, then you can use the initial keyword argument when instantiating the form for ModelB (e.g. in your view); like this:
def my_view(request):
"""
Save data to ModelA and show the user the form for ModelB
"""
if request.method == 'POST':
# Save ModelA instance
model_a_form = ModelFormA(request.POST)
if model_a_form.is_valid():
model_a_instance = model_a_form.save()
# Once saved, show the user the form for ModelB, prefilled
# with the result from the previous form
model_b_form = ModelFormB(initial={'model': model_a_instance})
# etc. Render the page as usual
else:
# ...
elif request.method == 'GET':
# ...
If, on the other hand, you don't want ModelA to appear in the second form as a field, but still link it to the resulting instance, you can do something like this when handling the response of the second form.
# ... instance of first model is saved in `model_a_instance`
# Initiate an empty instance of ModelB, with the value of `model` field set to model_a_instance
model_b_instance = ModelB(model=model_a_instance)
# Provide it as an instance to your model form
model_b_form = ModelFormB(request.POST, instance=model_b_instance)
if model_b_form.is_valid():
# Everything else should work fine.
Hope this helps.
Related
I followed this: https://www.yergler.net/2009/09/27/nested-formsets-with-django/ and this: django inline formsets with a complex model for the nested form and overall my code works great.
class Account(models.Model):
user_username = models.ForeignKey(User, on_delete=models.CASCADE)
account_name = models.CharField(max_length=30)
class Classification(models.Model):
user_username=models.ForeignKey(User, on_delete=models.CASCADE)
data_id=models.ForeignKey(ImportData, on_delete=models.CASCADE)
class ImportData(models.Model):
user_username = models.ForeignKey(User, on_delete=models.CASCADE)
data_id = models.UUIDField(
primary_key=True, default=uuid.uuid4, editable=False)
ClassificationFormset = inlineformset_factory(ImportData, Classification, exclude=('user_username',), extra=1)
# below is just what came from the nested formset links above: pasted here for easy reference.
class BaseNestedTransactionFormset(BaseInlineFormSet):
def add_fields(self, form, index):
# allow the super class to create the fields as usual
super(BaseNestedTransactionFormset, self).add_fields(form, index)
try:
instance = self.get_queryset()[index]
pk_value = instance.pk
except IndexError:
instance=None
pk_value = hash(form.prefix)
transaction_data = None
if (self.data):
transaction_data = self.data;
# store the formset in the .nested property
form.nested = [
CategoryFormset(data=transaction_data,
instance = instance,
prefix = 'CAT_%s' % pk_value)]
def is_valid(self):
result = super(BaseNestedTransactionFormset, self).is_valid()
for form in self.forms:
if hasattr(form, 'nested'):
for n in form.nested:
# make sure each nested formset is valid as well
result = result and n.is_valid()
return result
def save_new(self, form, commit=True):
"""Saves and returns a new model instance for the given form."""
instance = super(BaseNestedTransactionFormset, self).save_new(form, commit=commit)
# update the form’s instance reference
form.instance = instance
# update the instance reference on nested forms
for nested in form.nested:
nested.instance = instance
# iterate over the cleaned_data of the nested formset and update the foreignkey reference
for cd in nested.cleaned_data:
cd[nested.fk.name] = instance
return instance
def save_all(self, commit=True):
"""Save all formsets and along with their nested formsets."""
# Save without committing (so self.saved_forms is populated)
# — We need self.saved_forms so we can go back and access
# the nested formsets
objects = self.save(commit=False)
# Save each instance if commit=True
if commit:
for o in objects:
o.save()
# save many to many fields if needed
if not commit:
self.save_m2m()
# save the nested formsets
for form in set(self.initial_forms + self.saved_forms):
# if self.should_delete(form): continue
for nested in form.nested:
nested.save(commit=commit)
ImportTransactionFormset = inlineformset_factory(Account, ImportData, exclude=('user_username',), formset=BaseNestedTransactionFormset, extra=0)
My template has a table that displays the import data formset... user selects the account and the table shows all the imported data from that account. For each of these row forms, there is a hidden row underneath... user clicks a button to show that hidden row. The hidden row displays the nested classification formset.
If include the user_username field in the template and allow for it to be part of the nested formset in the template, i can set is accordingly in the html form and the formsets save no problem.
However: I want to be able to exclude the user_username field from the template and have my view or some other method under the BaseNestedTransactionFormset class set the value of the user_username field to request.user value for whoever is logged in at that time.
I tried to override the clean method, but cleaned_data kicks back an error because the form doesnt validate; the field is required. I can't seem to figure out a good way to do this.
If this was a normal formset, not too hard to do. I would just set the field by modifying what comes back from POST. I have never worked with nested inline formsets, and the prefixes and indeces in the field names have got me. I've been at this for a couple of days and can't seem to be getting anywhere.
I am also contemplating just getting rid of that field from the classification model, since it is already tied to the ImportData model which is linked to the logged in user regardless. I'm just thinking i may run into this at some point again, so maybe good to solve.
Thanks in advance.
I want to create a view function + template that displays a simple form (derived from a user model) and also captures the form submission. How do I do this using generic views in Django?
My user model is:
class User(models.Model):
email = models.EmailField(unique=True)
name = models.CharField(max_length=100)
I only need to capture the email field in the form.
I think there must be a simple way to do this using generic forms, however I'm not sure which one to use nor how to do it. The only other ways I know how to do it are:
1) Create UserForm explicitly and a single view function separating POST and GET requests. E.g., :
def contact(request):
if request.method == 'GET':
# display UserForm
....
elif request.method == 'POST':
# process form submission
....
2) Create two views (with seperate URLs) - one using generic view to display form and another view to receive form submission e.g.,:
class contact(generic.DetailView):
# display form from User model
model = User
....
def submit(request):
# process form submission
....
So, my two questions are:
can and how should this be implemented using ONLY a generic view?
which generic view should be used?
First part of the answer: use a single view. If you use a function view (which is by far the simplest solution), the canonical form-handling edit view looks like:
def myview(request, instance_id, ...):
instance = get_object_or_404(pk=instance_id)
if request.method == "POST":
form = MyForm(request.POST, ..., instance=instance)
if form.is_valid():
# assuming a ModelForm
form.save()
return redirect(somewhere)
# invalid forms will be re-rendered with the error messages
else:
form = MyForm(instance=instance)
return render(request, "myapp/mytemplate.html", {"form": form})
For a create view, you just remove all the instance_xxx parts. Or you can use the same view for both create and update making the instance_id optional:
def myview(request, instance_id=None, ...):
if instance_id is not None:
instance = get_object_or_404(pk=instance_id)
else:
instance = None
if request.method == "POST":
form = MyForm(request.POST, ..., instance=instance)
if form.is_valid():
# assuming a ModelForm
form.save()
return redirect(somewhere)
# invalid forms will be re-rendered with the error messages
else:
form = MyForm(instance=instance)
return render(request, "myapp/mytemplate.html", {"form": form})
If you want a class-based generic view they are documented here. I personally don't think there's much to gain from generic class-based views (except eventually headaches when you try to grasp the execution flow scattered amongst half a dozen base classes and mixins) but YMMV.
update
if I want to do some processing on the data (including adding in extra fields) before saving an instance to the DB, where would I do this?
Preferably in the form itself unless you need some other data that you don't want to pass to the form. For all forms you can process data at the validation stage. With a ModelForm you can also override the save() method itself:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('whatever', 'something')
def save(self, commit=True):
""" Save user and create a pro account """
instance = super(MyModelForm, self).save(commit=False)
instance.something_else = 42
if commit:
instance.save()
return instance
CreateView can work perfectly according to your requirements, You only need to create a form of contact models and success_url where user will redirect after form submission. It'll save automatic user data into models
class ContactView(generic.CreateView):
form_class = ContactForm
template_name = 'contact/index.html'
success_url = '/homepage/' . #Mention URL here
This can also be done using only CreateView.Specify email in fields as you need only the email field in the form.You can also process submitted form in form_valid method.
class UserCreate(CreateView):
model = User
fields = ['email']
success_url = '/your_success_url/'
#transaction.atomic
def form_valid(self, form):
new_user = form.save(commit=False)
# process your submitted form here.
# eg. add any extra fields as:
# new_user.something = something
new_user.save()
return super().form_valid(form)
I have two separate models:
1. A MyUser model which inherits from AbstractBaseUser, and has a field of profile_page = models.OneToOneField(Profile, null=True)
2. A Profile model with a user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True) relationship to the User model.
I am attempting to allow users of the site to edit their own Profile information by providing them a ProfileForm ModelForm.
In my user_profile/views.py I have this profile_edit FBV:
def profile_edit(request):
if request.method == 'POST':
form = ProfileForm(request.POST)
if form.is_valid():
form.instance.user = request.user
form.instance.save()
return redirect('/profile/edit')
else:
form = ProfileForm(instance=request.user)
print(request.user)
print('hello get req here')
context = {
'form': form,
}
return render(request, 'profile_edit.html', context)
When I attempt to update profile information in profile_edit.html, the POST data will go through the first time, but not get saved to the DB. On a second attempt, I receive a UNIQUE constraint failed: user_profile_profile.user_id error.
form.instance.save() is pointed to as the direct cause of the exception.
In my estimation the error has something to do with the fact that upon creation of a new user, an initial unique ID is created for the user. So when I try to save() the Profile object, I think it is attempting to save() a new User, thereby causing the Unique Constraint failure.
How can I configure the two models in such a way that upon creation of a new User, the User has the ability to update their own Profile information? What should change in my views.py?
You're doing two things wrong, both to do with the instance argument.
Firstly, in the GET block, you're passing request.user as the instance. But this is a Profile form; you need to pass the user profile, not the user itself:
form = ProfileForm(instance=request.user.profile)
Secondly, in the POST block, you're not passing an instance at all. This means that Django won't know to update an existing item, but will try and create a new one. Again you need to pass the profile:
form = ProfileForm(request.POST, instance=request.user.profile)
Note also though, you should consider whether you need a separate Profile model at all. Initially that was the recommended way to provide custom user information, but for several versions Django has given you a way to use a customised User model - which you are in fact doing. You probably want to put the profile data directly in MyUser, rather than in a separate model with a one-to-one relationship.
Edit after comment It sounds like you're not automatically creating a profile when you create the user. You could do something like this in the view:
def profile_edit(request):
try:
profile = request.user.profile
except Profile.DoesNotExist:
profile = Profile(user=request.user)
if request.method == 'POST':
form = ProfileForm(request.POST, instance=profile)
...
else:
form = ProfileForm(instance=profile)
I am learning Django so I don´t know about this.
What is happening is that I have two tables.
Table BlogPost : Save all post.
Table Categoria : Save the ID of the category of register post.
My model.py
class BlogPost(models.Model):
title=models.CharField(max_length=150)
author = models.ForeignKey(User)
categorias_post = models.ManyToManyField(Categoria)
body = RichTextField(('Content of post'))
creada_en = models.DateTimeField(auto_now_add=True)
actualizada_al = models.DateTimeField(auto_now=True)
My forms.py
class FormularioPost(forms.ModelForm):
class Meta:
model = BlogPost
fields = ('title', 'author', 'categorias_post', 'body')
My views.py
def postregistrado(request):
if request.method == "POST":
form = FormularioPost(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save
messages.success(request, 'Su post ha sido registrado con éxito.')
else:
form = FormularioPost()
return render_to_response(
"postRegistrado.html",
locals(),
context_instance=RequestContext(request),
)
I want to insert in two different tables from the same views.py. Can anyone help me with that?
When you are using commit=False, you have to explicitly call save_m2m() to save the many to many fields.
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save() #Note that it is a function call.
post.save_m2m()
You can read more on this in the documentation here
Another side effect of using commit=False is seen when your model has
a many-to-many relation with another model. If your model has a
many-to-many relation and you specify commit=False when you save a
form, Django cannot immediately save the form data for the
many-to-many relation. This is because it isn’t possible to save
many-to-many data for an instance until the instance exists in the
database.
To work around this problem, every time you save a form using
commit=False, Django adds a save_m2m() method to your ModelForm
subclass. After you’ve manually saved the instance produced by the
form, you can invoke save_m2m() to save the many-to-many form data.
Another thing is, make sure you add the login_required decorator on this view, so that you don't run into weird issues when post.author = request.user evaluates to anonymous users
I have a django model and a ModelForm. The model has about 10 fields, but I only want to show a few with the ModelForm for the user. So I have something like this:
class Create_EventForm(ModelForm):
class Meta:
model = Event
fields = ['event_name','event_datetime','event_venue','event_url','event_tags','event_zip','event_category','event_description']
However, I can't create an object based on this data, because there are still missing fields. When I receive the data via POST in my view I am doing one of these:
if request.user.is_authenticated:
if request.POST:
f = Create_EventForm(request.POST)
if f.is_valid():
ne = f.save()
What I want to do is right before the f.save() I want to do lookups based on existing data I have to pull in other data I need for the model and create the object. Any idea how this can be done?
Save the form with commit=False, edit the returned instance, then save it to the db.
if request.POST:
f = Create_EventForm(request.POST)
if f.is_valid():
event = f.save(commit=False)
event.other_value = lookup_value()
event.save()
See the doc's on ModelForm's save method for more info.