Add a button in a Django ModelForm - django

I have created a simple form in which the user is specifying the name of a (Bloomberg)Ticker. In a second field the user is entering an address of a webpage. Of course, she/he can manually correct it but I would like to offer him/her a button such that the app suggests the name of the page based on the content of first Ticker field, e.g. the user enters "CARMPAT FP Equity" and then the app suggests correctly
https://www.bloomberg.com/quote/CARMPAT:FP
# forms.py
from django.forms import ModelForm, ValidationError
from pybloomberg.bloomberg import valid_ticker
from .models import Symbol
class SymbolAdminForm(ModelForm):
# Have a button next to address field...
class Meta:
model = Symbol
fields = '__all__'
def clean(self):
cleaned_data = self.cleaned_data
ticker = cleaned_data.get('bloomberg_symbol')
return cleaned_data
def react(self):
# extract the content for the bloomberg_symbol field
address = ...
# manipulate the content for the address field
self.... = address

If you want the suggestion to be applied automatically after the form is posted, you can move your logic to the clean() method and you will need to allow the address field to be empty.
def clean(self):
cleaned_data = self.cleaned_data
ticker = cleaned_data.get('bloomberg_symbol')
cleaned_data['address'] = ticker_name_to_address()
return cleaned_data
If you want the suggestion to be displayed to the users when they enter the name, you will have no choice but to use JavaScript.

Related

Django Display User as user Full Name in Admin Field

I got stuck when trying to display User foreign as use full name
models.py
class Customer(models.Model):
name = models.charfield(max_length=100)
sales = models.foreignkey(User, on_delete=models.CASADE)
admin.py
#admin.register(Customer)
list_display = ('name', 'sales')
nah by default sales as user foreign display as username. But i want display it to first name or full name
Edited
You can add a method in the ModelAdmin, as mentioned by #dirkgroten in the comments below:
from django.contrib import admin
#admin.register(Customer)
class CustomerAdmin(admin.ModelAdmin)
list_display = ('name', 'sales_first_name')
def sales_first_name(self, obj):
return obj.sales.first_name + obj.sales_last_name
The upside is you can handle null values as you wish, if your foreign key is not required, e.g.
def sales_first_name(self, obj):
if obj.sales:
return obj.sales.first_name
else:
return None # or return 'nobody'
Old answer
(which, as mentioned in the comments, does not work (unless you use a plugin like django-related-admin))
Maybe use sales__first_name instead?
list_display = ('name', 'sales__first_name')
This way django will evaluate the first_name attribute of the user object instead of returning the default string representation (defined by the __str__ method in the user model, which returns just the username).

add a placeholder in these fields django

I'm really confused how to use placeholders. I'm using model that has a couple of fields of data and I want to have a place holder telling he user what to enter. So placeholder sounds like a good way to put this info into the text field. I'm using Crispy forms and fields come from the model.
Here is my forms.py just don't know how to incorporate placeholders here if its even here where I need to do it.
from django import forms
from .models import MRReg
class SignUpForm(forms.ModelForm):
class Meta:
model = MRReg
fields = ['SiteID','HeliumValue']
def clean_HeliumValue(self):
HeliumValue = self.cleaned_data.get('HeliumValue')
#HeliumValue_base, provider = HeliumValue.split("#")
#domain, extension = provider.split('.')
#if not extension == "edu":
#raise forms.ValidationError("Please use a valid .edu address")
return HeliumValue
#print (self.cleaned_data)
def clean_SiteID(self):
SiteID = self.cleaned_data.get('SiteID')
raise forms.ValidationError("Please enter a valid SiteID")
return SiteID
If you use forms widgets like forms.CharField you can pass this to it, for example forms.CharField()
forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'my-placeholder'}))

Django - ModelForm display the item but insert the foreignkey

I have two models as ProjectContacts and the User:
class ProjectContacts(models.Model):
project_contact_fname = models.CharField(max_length=120, blank=False)
project_contact_lname = models.CharField(max_length=120, blank=False)
project_contact_group = models.ForeignKey(ProjectGroup)
project_contact_title = models.ForeignKey(ProjectContactTitles)
project_site_admin = models.BooleanField(default=False)
project_contact_add_date = models.DateTimeField(auto_now=True)
project_contact_defined_by = models.ForeignKey(User)
In my form I am presenting the project_contact_defined_by field in the form as a read-only TextInput() in the form.py so that I can show the full name of the currently logged-in user in the form as readonly. However, in my view I have to set the initial field in order to show the full name. Below is my view.py
def add_project_contact(request):
..............
form = ProjectContactForm(initial={'project_contact_defined_by' : request.user.get_full_name(),
}, )
return render(request,'members/rfc/create.html',{'form': form})
Now, I am having and issue with the record insertion here and I think when the form data gets inserted it is the full name that gets inserted not the user.id? I would appreciate it if someone can help me out here to resolve this.
Rather than displaying the user as read only form field, it would probably be better to exclude the field from your form, and display the user's name in the template, wherever you wish, as follows: {{ request.user.get_full_name }}
Then, after you check that the form is valid, you would set the User as follows:
project_contact = form.save(commit=False) # This initializes the object with form data without saving, so you can edit or alter fields before the model is created
project_contact.project_contact_defined_by = request.user
project_contact.save()
You would also want to check that the user exists (and is not anonymous):
if user.is_authenticated():
# Check that form is valid, then create model
Note that if you are using a generic FormView, then you will be overriding form_valid() (which already checks that the form is valid) and use self.request.user to access the user.

How to use only form validation of ModelForm?

How to use only form validation of ModelForm?
Please see my code!
models.py
from django.db import models
class IPAddr(models.Model):
ip = models.GenericIPAddressField(unique=True,)
class myModel(models.Model):
ip = models.ForeignKey(IPAddr)
forms.py
from django.forms import ModelForm
from django import forms
from app.models import * # This is thing above models.py
class myModelForm(ModelForm):
ip = forms.GenericIPAddressField()
class Meta:
model = myModel
I want below logic.
user input ip(to ip field) -> validation -> (form.is_valid is True) goodIP = IPAddr.objects.get_or_create(user_input_ip) -> myModel.objects.create(ip=goodIP)
But validation is always fail...
Because (for example) user input is '1.2.3.4'. As you know, '1.2.3.4' is valid IP Address.
But form.is_valid is False. Because of '1.2.3.4' is not IPAddr instance.
So I can't use logic that I want.
Let's see ModelForm document.
The first time you call is_valid() or access the errors attribute of a ModelForm triggers form validation as well as model validation.
I want validation about only form validation...
...
Do I must use Forms not ModelForm...?
While using ModelForm, can I use my logic that I want?
class myModelForm(forms.ModelForm):
ip = forms.GenericIPAddressField()
class Meta:
model = myModel
def clean(self):
cleaned_data = super(ContactForm, self).clean()
ip = cleaned_data.get("ip")
//do your login here
return cleaned_data

Django "Enter a list of values" form error when rendering a ManyToManyField as a Textarea

I'm trying to learn Django and I've ran into some confusing points. I'm currently having trouble creating a movie using a form. The idea of the form is to give the user any field he'd like to fill out. Any field that the user fills out will be updated in its respective sql table (empty fields will be ignored). But, the form keeps giving me the error "Enter a list of values" when I submit the form. To address this, I thought stuffing the data from the form into a list and then returning that list would solve this.
The first idea was to override the clean() in my ModelForm. However, because the form fails the is_valid() check in my views, the cleaned_data variable in clean() doesn't contain anything. Next, I tried to override the to_python(). However, to_python() doesn't seem to be called.
If I put __metaclass__ = models.SubfieldBase in the respective model, I receive the runtime error
"TypeError: Error when calling the
metaclass bases
metaclass conflict: the metaclass of a derived class must be a
(non-strict) subclass of the
metaclasses of all its bases"
My approach doesn't seem to work. I'm not sure how to get around the 'Enter a list of values" error! Any advice?
Here is the relevant code (updated):
models.py
""" Idea:
A movie consists of many equipments, actors, and lighting techniques. It also has a rank for the particular movie, as well as a title.
A Theater consists of many movies.
A nation consists of many theaters.
"""
from django.db import models
from django.contrib.auth.models import User
class EquipmentModel(models.Model):
equip = models.CharField(max_length=20)
# user = models.ForeignKey(User)
class ActorModel(models.Model):
actor = models.CharField(max_length=20)
# user = models.ForeignKey(User)
class LightModel(models.Model):
light = models.CharField(max_length=20)
# user = models.ForeignKey(User)
class MovieModel(models.Model):
# __metaclass__ = models.SubfieldBase
rank = models.DecimalField(max_digits=5000, decimal_places=3)
title = models.CharField(max_length=20)
equipments = models.ManyToManyField(EquipmentModel, blank=True, null=True)
actors = models.ManyToManyField(ActorModel, blank=True, null=True)
lights = models.ManyToManyField(LightModel, blank=True, null=True)
class TheaterModel(models.Model):
movies = models.ForeignKey(MovieModel)
class NationModel(models.Model):
theaters = models.ForeignKey(TheaterModel)
=====================================
forms.py
"""
These Modelforms tie in the models from models.py
Users will be able to write to any of the fields in MovieModel when creating a movie.
Users may leave any field blank (empty fields should be ignored, ie: no updates to database).
"""
from django import forms
from models import MovieModel
from django.forms.widgets import Textarea
class MovieModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MovieModelForm, self).__init__(*args, **kwargs)
self.fields["actors"].widget = Textarea()
self.fields["equipments"].widget = Textarea()
self.fields["lights"].widget = Textarea()
def clean_actors(self):
data = self.cleaned_data.get('actors')
print 'cleaning actors'
return [data]
class Meta:
model = MovieModel
=============================================
views.py
""" This will display the form used to create a MovieModel """
from django.shortcuts import render_to_response
from django.template import RequestContext
from forms import MovieModelForm
def add_movie(request):
if request.method == "POST":
form = MovieModelForm(request.POST)
if form.is_valid():
new_moviemodel = form.save()
return HttpResponseRedirect('/data/')
else:
form = MovieModelForm()
return render_to_response('add_movie_form.html', {form:form,}, context_instance=RequestContext(request))
The probable problem is that the list of values provided in the text area can not be normalized into a list of Models.
See the ModelMultipleChoiceField documentation.
The field is expecting a list of valid IDs, but is probably receiving a list of text values, which django has no way of converting to the actual model instances. The to_python will be failing within the form field, not within the form itself. Therefore, the values never even reach the form.
Is there something wrong with using the built in ModelMultipleChoiceField? It will provide the easiest approach, but will require your users to scan a list of available actors (I'm using the actors field as the example here).
Before I show an example of how I'd attempt to do what you want, I must ask; how do you want to handle actors that have been entered that don't yet exist in your database? You can either create them if they exist, or you can fail. You need to make a decision on this.
# only showing the actor example, you can use something like this for other fields too
class MovieModelForm(forms.ModelForm):
actors_list = fields.CharField(required=False, widget=forms.Textarea())
class Meta:
model = MovieModel
exclude = ('actors',)
def clean_actors_list(self):
data = self.cleaned_data
actors_list = data.get('actors_list', None)
if actors_list is not None:
for actor_name in actors_list.split(','):
try:
actor = Actor.objects.get(actor=actor_name)
except Actor.DoesNotExist:
if FAIL_ON_NOT_EXIST: # decide if you want this behaviour or to create it
raise forms.ValidationError('Actor %s does not exist' % actor_name)
else: # create it if it doesnt exist
Actor(actor=actor_name).save()
return actors_list
def save(self, commit=True):
mminstance = super(MovieModelForm, self).save(commit=commit)
actors_list = self.cleaned_data.get('actors_list', None)
if actors_list is not None:
for actor_name in actors_list.split(","):
actor = Actor.objects.get(actor=actor_name)
mminstance.actors.add(actor)
mminstance.save()
return mminstance
The above is all untested code, but something approaching this should work if you really want to use a Textarea for a ModelMultipleChoiceField. If you do go down this route, and you discover errors in my code above, please either edit my answer, or provide a comment so I can. Good luck.
Edit:
The other option is to create a field that understands a comma separated list of values, but behaves in a similar way to ModelMultipleChoiceField. Looking at the source code for ModelMultipleChoiceField, it inhertis from ModelChoiceField, which DOES allow you to define which value on the model is used to normalize.
## removed code because it's no longer relevant. See Last Edit ##
Edit:
Wow, I really should have checked the django trac to see if this was already fixed. It is. See the following ticket for information. Essentially, they've done the same thing I have. They've made ModelMutipleChoiceField respect the to_field_name argument. This is only applicable for django 1.3!
The problem is, the regular ModelMultipleChoiceField will see the comma separated string, and fail because it isn't a List or Tuple. So, our job becomes a little more difficult, because we have to change the string to a list or tuple, before the regular clean method can run.
class ModelCommaSeparatedChoiceField(ModelMultipleChoiceField):
widget = Textarea
def clean(self, value):
if value is not None:
value = [item.strip() for item in value.split(",")] # remove padding
return super(ModelCommaSeparatedChoiceField, self).clean(value)
So, now your form should look like this:
class MovieModelForm(forms.ModelForm):
actors = ModelCommaSeparatedChoiceField(
required=False,
queryset=Actor.objects.filter(),
to_field_name='actor')
equipments = ModelCommaSeparatedChoiceField(
required=False,
queryset=Equipment.objects.filter(),
to_field_name='equip')
lights = ModelCommaSeparatedChoiceField(
required=False,
queryset=Light.objects.filter(),
to_field_name='light')
class Meta:
model = MovieModel
to_python AFAIK is a method for fields, not forms.
clean() occurs after individual field cleaning, so your ModelMultipleChoiceFields clean() methods are raising validation errors and thus cleaned_data does not contain anything.
You haven't provided examples for what kind of data is being input, but the answer lies in form field cleaning.
http://docs.djangoproject.com/en/dev/ref/forms/validation/#cleaning-a-specific-field-attribute
You need to write validation specific to that field that either returns the correct data in the format your field is expecting, or raises a ValidationError so your view can re-render the form with error messages.
update: You're probably missing the ModelForm __init__ -- see if that fixes it.
class MovieModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MovieModelForm, self).__init__(*args, **kwargs)
self.fields["actors"].widget = Textarea()
def clean_actors(self):
data = self.cleaned_data.get('actors')
# validate incoming data. Convert the raw incoming string
# to a list of ids this field is expecting.
# if invalid, raise forms.ValidationError("Error MSG")
return data.split(',') # just an example if data was '1,3,4'