create new parent model in CreateView in django - django

i am new to django and to this site, so apologies if this has been solved before but i haven't found it
So i have 2 django models
ModelA(Model):
ModelB(Model):
modelA = ForeignKey(ModelA, on_delete=models.CASCADE)
A form for the ModelB
ModelBForm(ModelForm):
class Meta:
model=ModelB
exclude=()
and a view
createModelBView(CreateView):
model = ModelB
form_class = ModelBForm
the template only does
{{form}}
When rendered, there is a dropdown list for the ModelA field so I can choose from existing instances of the ModelA, but what if a new one needs to be created? In the admin there is an option next to edit or create a new ModelA in a popup. Is there an option to do this with CreateView?
Thanks

There is no built-in functionality like that.
However you can build it easily yourself.
You will have to add a link (or a HTML form) in your template which points to the URL corresponding to the view you implemented to create the given model.
Following is a very abstract example.
In your template:
<form>
{{csrf_token}}
{{ form }}
Create model A if you want
<input type="submit" value="Submit">
<\form>
In your urls.py
url(r'^models/createA/$', views.CreateModelAView.as_view(), name="optional")
In your views.py
createModelAView(CreateView):
model = ModelA
form_class = ModelAForm
Then you'll need to create a form called ModelAForm.
On a different note, I'd suggest to start off with functional views if you're new to Django. It is more coding but you get a better feel of what's going on

In the admin there is an option next to edit or create a new ModelA in a popup. Is there an option to do this with CreateView?
No, not built in. That functionality in the admin involves a lot of front-end work involving templates and routing that would have to come from somewhere; since a Form/ModelForm instance can't assume it has access to the admin (which is a contrib module, may not be enabled, and is permission-sensitive), the infrastructure required for that can't be assumed to be available in the general case.
Keep in mind that {{ form }} doesn't even render <form> tags or any kind of submit element. It's intended to be a very, very basic way to render a very, very basic set of fields, while the admin is built specifically to be a (reasonably) powerful, flexible way to put a UI in front of your models.
You could certainly build that functionality yourself, or find a reusable app that does the same thing, but there is no facility distributed with Django to generate it automatically.

Related

Adding a new ManyToMany relationship in a form?

Django newbie here. I keep encountering the exact same design paradigm, which seems like it should be common for everyone, yet can't find out how it's supposed to be resolved.
Picture the following ManyToMany relationships:
An organization could have many members; each person could be a member of many organizations
An organization could manage many objects. An object could be in use by multiple organizations. The same applies to the relationship between people and objects.
An organization, person, or object could have multiple media elements (photos, videos, etc) of it, and a single media element could be tagged with numerous organizations, people, or objects
Nothing unusual. But how does a site user add a new person, organization, or object? It seems that if someone is filling out an "add an organization" form, in addition to choosing from existing people, objects, media, etc there should be a button for "new member", "new object", "new photo", etc, and this should take you to the forms for creating new members, objects, media, etc. And when they're done, it should go back to the previous page - whose form filled-out form entries should persist, and the newly created entry should be listed in its respective ManyToMany field.
The problem is, I don't know how to do this. I don't know how one would add a button in the middle of a form, and can't seem to find anything to clarify how to do it. I assume it would need to be a submit button, with a different name / id or some other way so that views.py can treat it differently, via flagging an "incomplete" record in the database. And the new form will need to be passed information about what page it needs to go back to when it's submitted.
Am I thinking about this correctly? If so, then I think the only knowledge I lack is how to add a second submit button in a form and how to recognize its usage in views.py.
If I'm not thinking about this correctly, however, please suggest an alternative paradigm that you think makes more sense :) This is my first Django project, so I'm learning as I do it.
ED: I'm thinking maybe instead of using {{ form.as_p }} to display it, I need to iterate over fields and use some logic to add the extra submit button in the middle as html: What's the best way to add custom HTML in the middle of a form with many fields?
Then I'll just need to figure out a way to detect which submit button was used and put some logic behind it to handle partially-submitted forms, redirecting to a form to create the relation, and then redirecting back on submit... I can probably figure this out...
The first thing I would recommend is to define your models. Lay them all out with the attributes you require. That'll be the foundation for everything else you want to accomplish. You can do everything you mentioned with Django... it's just a matter of coding it. As far as I know you would need to create each model instance separately, and then you can refer to already created instances in the create form for the Organization model for example. I would look into the docs for generic views that help you create objects easily. Then you can link to other create forms if you wish. I don't know how you can create multiple instances of different models in one form, and I don't think it would be the best way to do things even if you can. Here's an example of a model, a create form, a create view, and corresponding url:
# models.py
class Organization(models.Model):
name = models.CharField(max_length=100, null=True, blank=True)
# forms.py
class OrganizationForm(forms.ModelForm):
class Meta:
model = Organization
fields = ('name',)
def __init__(self, *args, **kwargs):
super(OrganizationForm, self).__init__(*args, **kwargs)
self.fields['name'].required = True
def clean(self):
cleaned_data = super(OrganizationForm, self).clean()
name = cleaned_data.get('name')
# views.py
class OrganizationCreateView(CreateView): # inherits from CreateView
form_class = OrganizationForm
template_name = 'create_org.html'
success_url = 'success'
def form_valid(self, form): # validate the form and save the model instance
org = form.save(commit=False)
org.save()
return redirect(reverse('redirect_url'))
# urls.py
from Project.apps.app_name import views as app_views
app_name = 'app_name'
urlpatterns = [
url(r'^create_org/$', app_views.OrganizationCreateView.as_view(), name='create_org'), # as_view() is used for class based views
# create_org.html
<form method="post">
{% crsf_token %}
{{ form.as_p }}
<a href="{% url 'app_name:create_person' %}>Create person</a> # You can link to other create views, and just style the link as a button.
<input type="submit" value="Submit">
</form>
Hope that helps.

Specifying specific form format in Django

I am using materializecss to give my django site some material elements. I have put together a form (the 'old' way using html) but now realised I need to use a django form instead. The problem is, these forms don't play well with materialises built in column system (they use classes to determine rows and column spacing). Here is an example of the layout I set up so far. However when defining the form through form.py, it spits out one input per layer.
My question is: what can I do to either a) get django to work with the html-defined form or b) make a 'form template' to give the input fields the appropriate classes?
If you want to see the code I can post some but I'm quite a new coder so it's messy.
Thanks!
There are three ways I can think of off the top of my head.
If you want full control over the HTML form, in a Django template or HTML form, simply map the names of your fields to match the underlying field names in the Django form. This way, when POSTed back to your view, Django will automatically link up the POSTed fields with the Django form fields.
For example, if you have a field username in your Django form (or Django model if using ModelForm), you could have an element <input type="text" name="username" maxlength="40"> (that you can style any way you need) on your HTML form that Django will happily parse into your Django form field, assuming your view is plumbed correctly. There is an example of this method in the Django documentation.
Another way is to customize the Django form field widgets in your Django form definition. The Django documentation talks a little bit about how to do this. This is great for one offs, but is probably not the best approach if you expect to reuse widgets.
The final approach would be to subclass Django form field widgets to automatically provide whatever attributes you need. For example, we use Bootstrap and have subclassed nearly all of the widgets we use to take advantage of Bootstrap classes.
class BootstrapTextInput(forms.TextInput):
def __init__(self, attrs=None):
final_attrs = {'class': 'form-control'}
if attrs is not None:
final_attrs.update(attrs)
super().__init__(attrs=final_attrs)
Then it's simply a matter of letting the Django form know which widget to use for your form field.
class UsernameForm(forms.ModelForm):
class Meta:
model = auth.get_user_model()
fields = ['username']
widgets = {'username': BootstrapTextInput()}
Hope this helps. Cheers!

Add a form to admin side

I need to create a form to admin-side with two fields
Number of code: integer
Value of code: float
How can I do that. This form is not related to any model.
You can implement your modelless form as explained in #levi's answer.
Then, you can place it in the admin site in a number of different ways, depending your needs:
Make instances of the form available to all templates via a context processor, and override the admin templates to have it rendered wherever you want. You can create a view for only processing the form.
Create a view for both rendering and processing the form in a unique place, and hook it up to the admin as explained in the old Django Book, you'll need to make sure the template for that view extends one of the admin's templates (admin/change_form.html may be a good choice).
from django import forms
class Your_Form(forms.Form):
number_code = forms.IntegerField()
value_code = forms.FloatField()

How to create new (unsaved) Django model with associations?

I'm building a "preview" function into the CMS for my website which uses the existing front-end template to render a model. This model has an association:
class FeatureWork(models.Model):
name = models.CharField(max_length=100)
...
class FeatureWorkLink(models.Model):
feature_work = models.ForeignKey(FeatureWork)
In the view for the preview, I'm trying to build the model such that when the template calls feature.featureworklink_set.all it returns the associated links. Since neither model has been saved yet, all the standard Django form techniques seem to go out the window.
This is what I have so far, but it blows up when I call the add method on the manager, since the parent hasn't been saved yet:
form = FeatureWorkAdminForm(initial=request.POST)
featured = form.save(commit=False)
for link in request.POST['links'].split(","):
featured.featureworklink_set.add(FeatureWorkLink(image=link))
Why don't you just add:
preview = BooleanField()
to your models, save everything to database and don't look for hacks. This way you can have drafts for free.
You can actually save it in transaction and rollback when template is ready. It's not very efficient, but at least it will work.
featured.featureworklink_set.add(FeatureWorkLink(image=link)) will immediately attempt to create a relationship between a FeatureWork and FeatureWorkLink, which isn't going to happen because that instance of FeatureWork is not present in the database and you cannot satisfy the predicates for building the foreign key relationship.
But the great thing is that Django's Model and ModelForm instances will not validate foreign key relationships until your actually trying to commit the data. So manually constructing your FeaturedWorkLink with an uncommited, non-existing FeatureWork should satisfy any presentation of the data you need to do, much to what you'd expect:
links = []
form = FeatureWorkAdminForm(initial=request.POST)
featured = form.save(commit=False)
for link in request.POST['links'].split(","):
links.add(FeatureWorkLink(image=link, feature_work=featured))
# then somewhere in your templates, from the context
{% for link in links %}
<img src="{{ link.image }}"
title="Image for the featured work: '{{ link.feature_work.name }}'" />
{% endfor %}
So basically, during the course of collecting the data to create a FeatureWork, you'll have to maintain the FeatureWorkLink instances through subsequent requests. This is where you'd use a model form set, but provide an uncommitted FeatureWork for the feature_work property for every model form instance of the set, up until the point where all the data has been collected, where you then provide a commited FeatureWork instance, so that the model form set can satisfy referential integrity and finally be commited to the database.

Hidden field in Django Model

A while back I made a Model class. I made several ModelForms for it and it worked beautifully.
I recently had to add another optional (blank=True, null=True) field to it so we can store some relationship data between Users. It's essentially a referral system.
The problem is adding this new field has meant the referral field shows up where I haven't changed the ModelForms to exclude it. Normally this would just mean an extra 10 minutes going through and excluding them but in this case, due to project management politics out of my control, I only have control over the Models for this application.
Can I either:
Set the field to auto-exclude?
Set it so it renders as a hidden (acceptable if not perfect)?
If you have access to the template you could render it as a hidden field with the following code:
{{ form.field_name.as_hidden }}
instead of the standard:
{{ form.field_name }}
from the docs on Using a subset of fields on the form:
Set editable=False on the model field. As a result, any form created from the model via ModelForm will not include that field.
You could define a custom model field subclass and override the formfield() method to return a field with a HiddenInput widget. See the documentation for custom fields.
Though you mentioned that you cannot use exclusion in your case, I think others who come across this answer (like myself, based on the title) may find it helpful.
It is possible to selectively hide fields using exclude in ModelAdmin, here is a snippet from something I'm working on:
class ItemsAdmin(admin.ModelAdmin):
form = ItemsForm
actions = None
list_display = ('item_id', 'item_type', 'item_title', 'item_size', 'item_color',)
search_fields = ('item_id', 'item_title',)
inlines = [ImageInline,]
readonly_fields = ('disable_add_date','disable_remove_date',)
exclude = ('add_date', 'remove_date',)
###.............