I am using createview for creating few fields of a model. I want to provide custom validation to attributes in my form. I am not sure how to do it through the CreateView. I don't want to create a Modelform for it.
Generally custom validation for the attributes is performed by clean_attr() method in forms. So, Is there any way to perform this in createview ?
my createview class
#method_decorator(never_cache, name='dispatch')
class AppCreateView(LoginRequiredMixin, CreateView):
model = models.App
fields = ['name', 'background', 'font', 'textcolor']
def get_context_data(self, *args, **kwargs):
context = super(AppCreateView, self).get_context_data(*args, **kwargs)
context['view'] = 'create'
return context
In the fields, I am excluding a fild called "date" (which has to be today). Is there any way to set the date attribute in CreateView ?
Thanks
update
My questions
How to make custom validation in AppCreateView ?
How to fill the other attributes apart of the user filled ones such as date ?
If you want to do that you can add validators on your model which will be catched by CreateView : https://docs.djangoproject.com/en/2.1/ref/validators/
For the date, juste add on your field :
my_field = models.DateTimeField(auto_now=True) #the date will be timezone.now() when you save the instance.
Related
Is it possible to load external data into a field for filling in?
Example: A field with for product names. However we already have the names of the products in another location, we just need to list these products within the field in the default django admin. Using resquets.
Thank you very much for your attention.
I think what you're looking for is how to customize the Django Admin, right? Check out this page in the documentation for a more detailed explanation, but here's an example that might help:
from django.contrib import admin
from .models import *
class ProductInline(admin.TabularInline):
model = Product
extra = 0
class OrderAdmin(admin.ModelAdmin):
inlines = [ProductInline]
admin.site.register(Order, OrderAdmin)
admin.site.register(Product)
This will show all of the products attached to a particular order when viewing that order from Django Admin.
You can prepopulate/fill a field in Django Admin with external data source. I guess you have some options defined somewhere outside your Django app and use those options as input for a charfield/integer field.
You can handle filling choices in a seperate Django form or overriding ModelAdmin methods. By creating a seperate form:
filter_choices = depends on your logic for loading external data
class AdminForm(forms.ModelForm):
filter_text = forms.ChoiceField(choices = filter_choices , label="Filter By",
widget=forms.Select(), required=True)
class Meta:
model = YourModel
#admin.register(YourModel)
class YourModelAdmin(admin.ModelAdmin):
form = AdminForm
You can try the 'formfield_for_foreignkey' method of the default ModelAdmin class
Example:
class MyModelAdmin(admin.ModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "car":
kwargs["queryset"] = Car.objects.filter(owner=request.user)
return super().formfield_for_foreignkey(db_field, request, **kwargs)
This example (from the original docs) will populate the 'car' field with only specific values.
Pls note that this method suits a foreinKey. I'm not sure if it fits your requirements.
Is there a best practice for ForeignKey unique=True values not to be displayed in the forms selection choice?
WarehouseBin = models.ForeignKey(WarehouseBin, unique=True)
It is annoying for the users to select options that they are not allowed in model.
Do I have to redefine in the view form values after it was initiated .
form = ***Form(instance=post)
form.fields['WarehouseBin'].queryset =***.objects.exclude(***)
Is there any other way?
The best practice to control the content of the foreign key fields is to override your form __init__() method. That way your form logic is nicely separated from your view, and you can reuse the same form in your view and admin if you want to.
from django.forms import ModelForm
from myapp.models import Article
# Create the form class.
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ['pub_date', 'headline', 'content', 'reporter']
def __init__(self, *args, **kwargs):
super(ArticleForm, self).__init__(*args, **kwargs)
# fetch only active reporters
self.fields['reporter'].queryset = Repoter.objects.filter(active=True)
For more details see: https://docs.djangoproject.com/en/dev/ref/forms/fields/#fields-which-handle-relationships
BTW, since you are interested in best practices, here's another tip. You shouldn't use camel case for your class fields, but lowercase with words separated with underscore:
warehouse_bin = models.ForeignKey(WarehouseBin, unique=True)
See PEP8 for more details.
When I display a user profile inline in the admin, I want to delete some non-model fields based on certain conditions.
class ProfileInline(admin.StackedInline):
model = Profile
form = ProfileForm
max_num = 1
can_delete = False
The ProfileForm has some additional fields, which are not related to the model and I want to delete them under certain conditions, e.g. a certain user is editing the form.
I tried to explicitly set the fields to include and I also tried to exclude the fields with the help of get_formset():
def get_formset(self, request, obj=None, **kwargs):
kwargs['fields'] = ['fieldX', 'fieldY']
#kwargs['exclude'] = ['fieldZ']
return super(ProfileInline, self).get_formset(request, obj, **kwargs)
It works fine with model fields, but the non-model fields are always shown.
This is an old topic, but I think I can help.
You can hide the fields using the fieldsets property of the ModelAdmin class.
Just lists the fields that you want to show.
The code I would like to get is for a page that has a simple form of one field to change a user's email address using an UpdateView.
Sounds simple, but the difficulty is that I want the URL mapping url(r'email/(?P<pk>\d+)/$', EmailView.as_view(),) not to use the id of the Model used in my ModelForm (User) but the id of another Model (Profile).
The id of a Profile instance of a specific user can be called as follows inside a view: self.user.get_profile().id. I am using the Profile model of the reusable app userena if you are wondering.
A (afaik not optimally implemented ¹) feature of an UpdateView is that if you want to use your own ModelForm instead of letting the UpdateView derive a form from a Model you need to(otherwise produces an Error) define either model, queryset or get_queryset.
So for my EmailView case I did the following:
forms.py
class EmailModelForm(forms.ModelForm):
class Meta:
model = User
fields = (
"email",
)
def save(self, *args, **kwargs):
print self.instance
# returns <Profile: Billy Bob's Profile> instead of <User: Billy Bob> !!!
return super(EmailModelForm, self).save(*args, **kwargs)
views.py
class EmailView(UpdateView):
model = Profile # Note that this is not the Model used in EmailModelForm!
form_class = EmailModelForm
template_name = 'email.html'
success_url = '/succes/'
I then went to /email/2/. That is the email form of the user that has a profile with id 2.
If I would run a debugger inside EmailView I get this:
>>> self.user.id
1
>>> profile = self.user.get_profile()
>>> profile.id
2
So far so good. But when I submit the form it won't save. I could overwrite the save method in the EmailModelForm but I'd rather override something in my EmailView. How can I do that?
¹ Because UpdateView could just derive the model class from the ModelForm passed to the form_class attribute in case it is a ModelForm.
Having your view and model form correspond to different models seems a bad idea to me.
I would set model = User in your EmailView, then override get_object so that it returns the user corresponding to the given profile id.
In the Django admin, I would really like to be able to display an editable ManyToManyField in the list display.
It doesn't necessarily need to be the full ManyToManyField control - being able to save just one value would be good enough for the purposes of the list display (though the underlying values are many-to-many in nature).
My model looks like this:
class Item(models.Model):
name = models.CharField(max_length=500)
colour = models.ManyToManyField(Colour, related_name='primary_colour')
If I try this in admin.py:
class ItemAdmin(admin.ModelAdmin):
list_display = ('name', 'colour')
list_editable = ('colour')
Then I get this error:
'ItemAdmin.list_display[6]', 'colour' is a ManyToManyField which is not supported.
Is there any way at all that I can show an editable ManyToManyField for rapid editing in the list display?
I found this related question, which explains how to make the values visible in the list display, but not editable: ManyToManyField widget in a django admin change list?
Django by default won't allow to add ManyToManyField in list_editable in ModelAdmin. So we need to override model admin methods.
On looking your models you need to follow below steps to get the ManyToManyField editable in list display page.
In apps/forms.py you need to define which ManyToMany fields you need to make editable in list display page. As below,
from django import forms
from app.models import Item
class ItemChangeListForm(forms.ModelForm):
# here we only need to define the field we want to be editable
colour = forms.ModelMultipleChoiceField(queryset=Colour.objects.all(),
required=False)
In app/admin.py you need to override methods of model admin. As below,
from django.contrib import admin
from django.contrib.admin.views.main import ChangeList
from app.models import Item
from app.forms import ItemChangeListForm
class ItemChangeList(ChangeList):
def __init__(self, request, model, list_display,
list_display_links, list_filter, date_hierarchy,
search_fields, list_select_related, list_per_page,
list_max_show_all, list_editable, model_admin):
super(ItemChangeList, self).__init__(request, model,
list_display, list_display_links, list_filter,
date_hierarchy, search_fields, list_select_related,
list_per_page, list_max_show_all, list_editable,
model_admin)
# these need to be defined here, and not in ItemAdmin
self.list_display = ['action_checkbox', 'name', 'colour']
self.list_display_links = ['name']
self.list_editable = ['colour']
class ItemAdmin(admin.ModelAdmin):
def get_changelist(self, request, **kwargs):
return ItemChangeList
def get_changelist_form(self, request, **kwargs):
return ItemChangeListForm
admin.site.register(Item, ItemAdmin)
Now you all set to check the changes, run server and check django admin for Movie model. You can edit ManyToMany field directly from list display page.
Note : If you are going to use muliptle ManyToManyFields editable in list then, you need to set DATA_UPLOAD_MAX_NUMBER_FIELDS in settings.py .
You can easily add a custom view to your admin urls and add the required html/javascript/ajax. Here's the basics:
class ItemAdmin(admin.ModelAdmin):
# regular stuff
def render_foo(self, obj):
# add this to your list_display
html = '<stuff><input/submit action></stuff>'
return mark_safe(html)
def get_urls(self):
urls = super(ItemAdmin, self).get_urls()
extra_urls = patterns('',
(r'^process_foo/$', self.admin_site.admin_view(self.process_foo)),
)
return extra_urls + urls
def process_foo(self, request):
if not request.is_ajax():
raise Http404
foo = request.GET.get("attr")
# process m2m
# return some json