Im trying to implement auocomplete in my django project.
I want to pass a parameter through the url in the form. This is my Form
class SongForm(forms.ModelForm):
song_title = forms.ModelChoiceField(
queryset=Snr.objects.all(),
widget=autocomplete.ModelSelect2(url='login:song-autocomplete',attrs={'data-placeholder': 'Autocomplete ...',
'data-minimum-input-length': 3,},)
)
class Meta:
model = Song
fields = ['song_title']
my url pattern is
url(r'^(?P<playlist_id>[0-9]+)/create_song/song-autocomplete/$', views.SongAutocomplete.as_view(), name='song-autocomplete', ),
so while calling the url song-autocomplete it needs a parameter playist_id. How do I send it using the url='login:song-autocomplete'?
Thank you.
full example using my own code:
forms.py
class AddRelationshipForm(autocomplete.FutureModelForm):
first = autocomplete.QuerySetSequenceModelField(
queryset=autocomplete.QuerySetSequence(Person.objects.all()),
required=False,
widget=autocomplete.QuerySetSequenceSelect2(
url='PersonAutoUrl', attrs={
'style':'width:15em;', 'data-placeholder': 'find'
}
),
)
def __init__(self, *args, **kwargs):
type = kwargs.pop('type')
super(AddRelationshipForm, self).__init__(*args, **kwargs)
self.fields['first'] = autocomplete.QuerySetSequenceModelField(
queryset=autocomplete.QuerySetSequence(Person.objects.all()),
required=False,
widget=autocomplete.QuerySetSequenceSelect2(
url='PersonAutoUrl/'+type, attrs={
'style':'width:15em;', 'data-placeholder': 'search'
}
),
)
class Meta:
model = Relation
fields = ('first',)
urls.py
url(
'^relationships/RelationshipAutoUrl/(?P<type>\w+)/$',
RelationshipAutoView.as_view(),
name='PersonAutoUrl',
),
views.py
class RelationshipAutoView(Select2QuerySetSequenceView):
def get_queryset(self):
excluded = Person.objects.filter(first__owner=self.request.user, first__type=self.kwargs['type'])
queries = Person.objects.exclude(id__in=excluded)
if self.q:
queries = queries.filter(first_name__icontains=self.q)
return queries
initiating form and passing kwarg type in view:
def relationships(request):
relationships = ['Friend', 'Family','Business']
forms = {}
for key in relationships:
forms[key] = AddRelationshipForm(request.POST or None, prefix='new'+key, type=key)
Related
I wish to only display a subset of the choices for a model form field. E.g Depending on the url the user is at I might want only 'weight gain' and 'parkinsonism' displayed as options for the 'se_name' field.
I can work out how to get the url as a parameters in the view (p = self.request.GET.get("p", None)) But I cant work out how to use this parameter to limit the choices available.
This is the formset
SideeffectFormSet = inlineformset_factory(
Case,
SideEffect,
fields=("se_name",),
widgets={'concern': RangeInput()},
extra=0,
min_num=1,
validate_min=True,
)
Which is based on the model:
class SideEffect(TimeStampedModel):
SE_CHOICES = [
("weight_gain", "Weight Gain"),
("parkinsonism", "Parkinsonism"),
("dystonia", "Dystonia"),
("none", "None"),
]
se_name = models.CharField("",max_length=200, choices=SE_CHOICES, default="none")
case = models.ForeignKey(Case, on_delete=models.CASCADE)
And the form is rendered by this view:
class CaseView(LoginRequiredMixin, TemplateView):
model = Case
template_name = "se_balance/se_balance.html"
def get(self, *args, **kwargs):
p = self.request.GET.get("p", None)
sideeffect_formset = SideeffectFormSet(queryset=SideEffect.objects.none(),)
return self.render_to_response(
{ "sideeffect_formset": sideeffect_formset,
"sideeffect_formsethelper": SideEffectFormSetSetHelper,
}
)
To change the choices of a field in a formset dynamically you need to define a custom form class that does the choice alteration on __init__
from django import forms
from .models import SideEffect
class SideEffectForm(ModelForm):
class Meta:
model = SideEffect
fields = ['se_name']
def __init__(self, *args, p, **kwargs):
super().__init__(*args, **kwargs)
if p == 'foo':
self.fields['se_name'].choices = [
("weight_gain", "Weight Gain"),
("parkinsonism", "Parkinsonism"),
]
else:
...
Use the form class in your formset
SideeffectFormSet = inlineformset_factory(
Case,
SideEffect,
form=SideEffectForm,
extra=0,
min_num=1,
validate_min=True,
)
Then in your view you can pass custom parameters to the formset form
sideeffect_formset = SideeffectFormSet(queryset=SideEffect.objects.none(), form_kwargs={'p': p})
I am trying to dynamically change the viewable choices in a django form choice field depending on the user. Here is my form:
SubmitForm(forms.ModelForm):
class Meta:
model = Lesson
fields = (
"status",
)
Here is my models.py for Lesson:
CLASS_STATUS = (
('Attended', 'Attended'),
('Absent', 'Absent'),
('Teacher Postponed', 'Teacher Postponed'),
('Student Postponed', 'Student Postponed'),
)
status = models.CharField(
max_length=30,
choices=CLASS_STATUS,
null=True,
blank=True,
)
So, basically I want to be able to select which choices to show for each type of user. In order to do this, I hope you guys could also show how I could access the request.user information in my form. Thank you, and please ask me any questions you have.
Here is a part of the view as some of you requested:
class SubmitView(generic.UpdateView):
template_name = "agents/submit_form.html"
queryset = Lesson.objects.all()
context_object_name = "lesson"
form_class = SubmitForm
I have tried and failed to get this working with a ModelForm. I think your best answer is to retreat to using a vanilla Form, and manually update the object. Something like
class Status_form( forms.Form):
status = forms.ChoiceField( choices=Lesson.STATUS_CHOICES)
class UpdateLessonView( FormView):
form_class = Status_form
template_name = "agents/submit_form.html"
def form_valid( self, form):
self.lesson.status = form.cleaned_data['status']
self.lesson.save()
return redirect(
'lessons:lesson_detail', lesson=self.lesson.pk )
def get_form( self, form_class=None):
self.lesson = get_object_or_404( Lesson, id=self.kwargs['lesson'] )
self.initial = { 'status': self.wafer.status }
form = super().get_form( form_class)
# modify choices depending on user
if self.request.user ... :
status_choices=( (..., ...), ... )
form.fields['status'].choices = status_choices
return form
I have a DRF API that takes in the following model:
class Points(models.Model):
mission_name = models.CharField(name='MissionName',
unique=True,
max_length=255,
blank=False,
help_text="Enter the mission's name"
)
# Some irrlevant feid
url = models.URLField(help_text='Leave Empty!', default=" ")
date_added = models.DateTimeField(default=timezone.now)
class Meta:
get_latest_by = 'date_added'
And it's serializer:
from rest_framework.serializers import HyperlinkedModelSerializer
from .models import Points
class PointsSerializer(HyperlinkedModelSerializer):
class Meta:
model = Points
fields = (
'id', 'MissionName', 'GDT1Latitude', 'GDT1Longitude',
'UavLatitude', 'UavLongitude', 'UavElevation', 'Area',
'url', 'date_added'
)
And the view:
class PointsViewSet(ModelViewSet):
# Return all order by id, reversed.
queryset = Points.objects.all().order_by('-id')
serializer_class = PointsSerializer
data = queryset[0]
serialized_data = PointsSerializer(data, many=False)
points = list(serialized_data.data.values())
def retrieve(self, request, *args, **kwargs):
print(self.data)
mission_name = self.points[1]
assign_gdt = GeoPoint(lat=self.points[2], long=self.points[3])
gdt1 = [assign_gdt.get_lat(), assign_gdt.get_long()]
assign_uav = GeoPoint(lat=self.points[4], long=self.points[5], elevation=self.points[6])
uav = [assign_uav.get_lat(), assign_uav.get_long(), assign_uav.get_elevation()]
area_name = f"'{self.points[-2]}'"
main = MainApp.run(gdt1=gdt1, uav=uav, mission_name=mission_name, area=area_name)
print('file created')
return render(request, main)
I want to update the URL field of the file to contain a constant pattern and format in the end the mission_name field.
object.url = f'127.0.0.1/twosecondgdt/{mission_name}'
How can that be achieved and where should I store such code, the views.py or serializers.py?
There are several ways this could be achieved based on your requirements.
If you want to set the url upon creation even if it is not through the api, you can do it in the save method of the model itself:
class Points(models.Model):
# fields here
def save(self, **args, **kwargs):
if not self.url.strip():
# You may want to store the value of `127...` in an environment variable
self.url = f"127.0.0.1/twosecondgdt/{self.mission_name}"
super().save(*args, **kwargs)
If you want to set it through the view/serializer, you can set it in the create method of your serializer:
class PointsSerializer(HyperlinkedModelSerializer):
def create(self, validated_data):
mission_name = validated_data["mission_name"]
validated_data["url"] = f"127.0.0.1/twosecondgdt/{mission_name}"
return super().create(validated_data)
You can also override some methods in your viewset like perform_create or create
In my code CreateNoticeForm is working fine and all data gets saved perfectly except
many to many field which is tags in my notice model.Although it is working on admin site and gets saved.
here is my code
models.py
from django.db import models
from django.contrib.auth.models import User
from wysihtml5.fields import Wysihtml5TextField
# Create your models here.
class Tag(models.Model):
# For notice tags
name = models.CharField(max_length=70, unique=True)
def __str__(self):
return self.name
class Notice(models.Model):
# Notice Store
headline = models.CharField(max_length=140)
description = Wysihtml5TextField()
file_name = models.FileField(upload_to='static/noticefiles/', blank=True)
created_by = models.ForeignKey(User)
fors = models.CharField(max_length=1, choices=(('F','Faculty'),('S','Student'),) )
last_date = models.DateField()
tags = models.ManyToManyField(Tag, blank=True)
post_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now=True)
def __str__(self):
return self.headline
forms,py
FORS_CHOICES = (('F','Faculty'),('S','Student'))
class CreateNoticeForm(ModelForm):
fors = forms.ChoiceField(label="Related To",
choices=FORS_CHOICES,
)
class Meta:
model = Notice
fields = ('headline', 'description',
'fors', 'last_date', 'tags','file_name')
widgets = {
'description': Wysihtml5BootstrapWidget(),
'last_date': SelectDateWidget()
}
def __init__(self, *args, **kwargs):
super(CreateNoticeForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = 'create_notice_form_id'
self.helper.form_class = 'form-horizontal'
self.helper.form_method = 'post'
self.helper.label_class = 'col-lg-2'
self.helper.field_class = 'col-lg-8'
self.helper.layout = Layout(
Fieldset('Create Notice',
'headline',
'description',
Field('fors', label='Audience'),
MultiWidgetField('last_date',
attrs=(
{'style': 'width: 33.33%; display: inline-block;'}
)
),
'tags',
'file_name',
FormActions(
Submit('save', 'Create Notice',
css_class='btn-warning col-lg-offset-2'),
),
),
views.py
def create_notice(request):
context = RequestContext(request)
posted = False
if request.method=='POST':
create_notice_form = CreateNoticeForm(data=request.POST, files=request.FILES)
if create_notice_form.is_valid():
cnf = create_notice_form.save(commit=False)
cnf.created_by = request.user
cnf.save()
posted = True
else:
print(create_notice_form.errors)
else:
create_notice_form = CreateNoticeForm()
return render_to_response('notices/createnotice1.html',
{'create_notice_form': create_notice_form,
'posted': posted,},
context)
You have to call save_m2m():
cnf = create_notice_form.save(commit=False)
cnf.created_by = request.user
cnf.save()
create_notice_form.save_m2m()
Excerpt from the documentation:
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.
You have to Call the following after validating the form:-
if create_notice_form.is_valid():
parent = create_notice_form.save(commit=False)
parent.save()
create_notice_form.save_m2m()
I hope this will help you
note : This is closely related to the answer in this question :
django admin - add custom form fields that are not part of the model
In Django it is possible to create custom ModelForms that have "rouge" fields that don't pertain to a specific database field in any model.
In the following code example there is a custom field that called 'extra_field'. It appears in the admin page for it's model instance and it can be accessed in the save method but there does not appear to be a 'load' method.
How do I load the 'extra_field' with data before the admin page loads?
# admin.py
class YourModelForm(forms.ModelForm):
extra_field = forms.CharField()
def load(..., obj):
# This method doesn't exist.
# extra_field = obj.id * random()
def save(self, commit=True):
extra_field = self.cleaned_data.get('extra_field', None)
return super(YourModelForm, self).save(commit=commit)
class Meta:
model = YourModel
class YourModelAdmin(admin.ModelAdmin):
form = YourModelForm
fieldsets = (
(None, {
'fields': ('name', 'description', 'extra_field',),
}),
)
source code by #vishnu
Override the form's __init__ method and set the initial property of the field:
class YourModelForm(forms.ModelForm):
extra_field = forms.CharField()
def __init__(self, *args, **kwargs):
super(YourModelForm, self).__init__(*args, **kwargs)
initial = '%s*rnd' % self.instance.pk if self.instance.pk else 'new'
self.fields['extra_field'].initial = initial