I am doing a management tool web application. I would like user can click 'add members' button, and the member will be added under 'Members'. I am using ajax to retrieve data from database, but I do not know how to display 'username'. It only displays 'id'. Moreover, I would like added members to stay in page whenever user refreshes the page.
project_index.html
<form action="" method="GET" id="selection-form">
{% csrf_token %}
<select id="member_list">
{% for user in user %}
<option value="{{user.pk }} }}">
{{ user.username }}
</option>
{% endfor %}
</select>
<input type="button" value="Add member" id="selection-button">
</form>
<div id="res"> </div>
views.py
def member_select(request):
selection = request.GET.get('id',None)
if selection:
data = serializers.serialize('json',User.objects.filter(pk=selection))
else:
data = {}
return HttpResponse(data, content_type='application/json')
base.html
<script>
var url = $( '#selection-button' ).attr( 'action' );
$("#selection-button").on('click', function(e) {
e.preventDefault();
var value =$('#member_list').val();
console.info('test',value)
$.ajax({
type:'GET',
url:'.',
data:{
id:value,
},
success:function (result) {
$("#res").append(value);
console.info(result)
},
error:function (result) {
alert('error');
}
});
});
</script>
You Can write a serializer, and specify fields you want to display:
class UserSerializer:
class Meta:
model = User
fields = ('pk', 'username')
class UserSerializer:
class Meta:
model = User
fields = ('pk', 'username')
def create(self,validated_data):
return User(**validated_data)
For view
class UserCreateAPIView(generic.CreateAPIView):
serializer_class=UserSerializer
And I hope you was familiar with django rest
I have 2 concerns about how you are doing this:
First is that in your javascript you're reading the data wrong.
I inspected the result return from django.core.serializers.serialize the result only have those keys ['model', 'pk', 'fields'].
this is why you can access pk attribute in your javascript but not username that is because username attribute is part of fields object.
so you should read username as user.fields.username not user.username
Second concern is that your version is not retrieving all fields by default:
Try to user fields keyword argument:
https://docs.djangoproject.com/en/2.0/topics/serialization/#subset-of-fields
If you only want a subset of fields to be serialized, you can specify a fields argument to the serializer:
from django.core import serializers
...
data = serializers.serialize('xml', SomeModel.objects.all(), fields=('name','size'))
In this example, only the name and size attributes of each model will be serialized. The primary key is always serialized as the pk element in the resulting output; it never appears in the fields part.
So in your case this should be something like
data = serializers.serialize('json', User.objects.filter(pk=selection), fields=('username','id'))
I recommend that you use django-rest-framework just as #mohammad-ali described.
djanog-rest-framework allows you to do more.
Related
I am making a site on Django. And I faced a problem. I would like to create an input field in the form so that it is initially empty, but so that in the process of how the user enters data into it, a frame will appear next to it, with possible selection options based on data already entered by the user.
In short, I want to make something like this field
Help me please
The Select2 library is one that does a lot of magic for you. https://django-select2.readthedocs.io/en/latest/
An example from their own docs:
forms.py
class AuthorWidget(s2forms.ModelSelect2Widget):
search_fields = [
"username__icontains",
"email__icontains",
]
class BookForm(forms.ModelForm):
class Meta:
model = models.Book
fields = "__all__"
widgets = {
"author": AuthorWidget,
}
urls.py
urlpatterns = [
path("select2/", include("django_select2.urls")),
....
]
template.html
<h1>Create a new Book</h1>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit">
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
{{ form.media.js }}
I am trying to display a User's name on top of a box where they enter their Employee # in a form, without having to refresh the page.
For example, they enter their # and then after they click/tab onto the next field, it renders their name on top, which comes from the database, so the user knows they've entered the correct info. This name is stored in a separate model, so I try to retrieve it using the "id/number".
I am not too familiar with AJAX but after reading a few similar questions it seems like an AJAX request would be the most appropriate way to achieve this. I tried to make a function get_employee_name that returns the name of the person based on the way I saw another ajax request worked, but I'm not sure how to implement this so it displays after the # is entered.
My page currently loads, but when I check the network using F12, there is never a call to the function/url that searches for the name to display it on the page. I'm not sure where I might be missing the part that connects these two areas of the code, but I have a feeling it has to do with the html tag where the call is supposed to happen, as I am not too familiar with html and Django.
models.py
class EmployeeWorkAreaLog(TimeStampedModel, SoftDeleteModel, models.Model):
employee_number = models.ForeignKey(Salesman, on_delete=models.SET_NULL, help_text="Employee #", null=True, blank=False)
work_area = models.ForeignKey(WorkArea, on_delete=models.SET_NULL, null=True, blank=False)
station_number = models.ForeignKey(StationNumber, on_delete=models.SET_NULL, null=True, blank=True)
This is the model where the name is stored
alldata/models.py
class Salesman(models.Model):
slsmn_name = models.CharField(max_length=25)
id = models.IntegerField(db_column='number', primary_key=True)
I was reading I can add to the "attrs" in the widget an 'onchange' part, but I am not too familiar with how to approach this and tying it to the ajax request from forms and not the html.
forms.py
class WarehouseForm(AppsModelForm):
class Meta:
model = EmployeeWorkAreaLog
widgets = {
'employee_number': ForeignKeyRawIdWidget(EmployeeWorkAreaLog._meta.get_field('employee_number').remote_field, site, attrs={'id':'employee_number_field'}),
}
fields = ('employee_number', 'work_area', 'station_number')
views.py
class EnterExitArea(CreateView):
model = EmployeeWorkAreaLog
template_name = "operations/enter_exit_area.html"
form_class = WarehouseForm
def form_valid(self, form):
# do submission stuff..
def get_employee_name(request):
employee_number = request.GET.get('employee_number')
try:
employee = Salesman.objects.get(id=employee_number)
except Salesman.DoesNotExist:
return JsonResponse({'error': 'Employee not found'}, status=404)
employee_name = employee.slsmn_name
return JsonResponse({'employee_name': employee_name})
urls.py
urlpatterns = [
url(r'enter-exit-area/$', EnterExitArea.as_view(), name='enter_exit_area'),
path('ajax/load-stations/', views.load_stations, name='ajax_load_stations'),
path('get-employee-name/', views.get_employee_name, name='ajax_get_employee_name'),
]
The ajax request I tried to create is at the end of this html. I modified a similar request I found, but it does not actually display anything on the screen, not sure if I'm missing an area where the request is actually never being called, as I am not too familiar with how these types of requests work.
enter_exit_area.html
{% extends "base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" data-stations-url="{% url 'operations:ajax_load_stations' %}" novalidate >
{% csrf_token %}
<div>
<h1 get-employee-name-url="{% url 'operations:ajax_get_employee_name' %}"></h1>
<div>
{{ form.employee_number.help_text }}
{{ form.employee_number }}
</div>
<div>
{{ form.work_area.help_text }}
{{ form.work_area }}
</div>
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
</div>
</div>
</form>
<!-- This is the dependent dropdown script -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$("#id_work_area").change(function () {
var url = $("#warehouseForm").attr("data-stations-url");
var workAreaId = $(this).val();
$.ajax({
url: url,
data: {
'work_area': workAreaId
},
success: function (data) {
$("#my-hidden-div").show(); // show it
$("#id_station_number").html(data);
// Check the length of the options child elements of the select
if ($("#id_station_number option").length == 1) {
$("#id_station_number").parent().hide(); // Hide parent of the select node
} else {
// If any option, ensure the select is shown
$("#id_station_number").parent().show();
}
}
});
});
</script>
<!-- -->
<script>
$("#id_employee_number").change(function () {
var employee_number = $(this).val();
var url = $("#warehouseForm").attr("get-employee-name-url");
$.ajax({
url: url,
type:'GET',
data: {
'id': employee_number
},
success: function (data) {
var employee_name = data['employee_name'];
$('#employee_name')[0].innerHTML = employee_name;
},
error : function (data) {
var error_message = data['error'];
$('#employee_name')[0].innerHTML = error_message;
}
});
});
</script>
{% endblock main %}
What could be causing nothing to render on the page? Is there a call missing in the html portion?
I assume there needs to be a place where the onchange() goes, but I'm not sure where this would be in since the form fields are it's own thing, without tags.
With regards to
var url = $("#warehouseForm").attr("get-employee-name-url");
The id is referring to the form which does not have an attribute name "get-employee-name-url". Your url is actually in here
<h1 get-employee-name-url="{% url 'operations:ajax_get_employee_name' %}"></h1>
So maybe add an id to it like this
<h1 id="url" get-employee-name-url="{% url 'operations:ajax_get_employee_name' %}"></h1>
and then you can access it like you were
var url = $("#url").attr("get-employee-name-url");
I am trying to display a User's name on top of a box where they enter their Employee # in a form, without having to refresh the page.
For example, they enter their # and then after they click/tab onto the next field, it renders their name on top, which comes from the database, so the user knows they've entered the correct info. This name is stored in a separate model, so I try to retrieve it using the "id/number".
I am not too familiar with AJAX but after reading a few similar questions it seems like an AJAX request would be the most appropriate way to achieve this. I tried to make a function get_employee_name that returns the name of the person based on the way I saw another ajax request worked, but I'm not sure how to implement this so it displays after the # is entered.
I get this error when trying to see the page now, I'm not sure where I'm passing the "id/employee_number" incorrectly which is causing this to show:
django.urls.exceptions.NoReverseMatch: Reverse for 'ajax_get_employee_name' with no arguments not found. 1 pattern(s) tried: ['operations/ajax\\/get\\-employee\\-name\\/(?P<id>[0-9]+)\\/$']
models.py
class EmployeeWorkAreaLog(TimeStampedModel, SoftDeleteModel, models.Model):
employee_number = models.ForeignKey(Salesman, on_delete=models.SET_NULL, help_text="Employee #", null=True, blank=False)
work_area = models.ForeignKey(WorkArea, on_delete=models.SET_NULL, null=True, blank=False)
station_number = models.ForeignKey(StationNumber, on_delete=models.SET_NULL, null=True, blank=True)
def __str__(self):
return self.employee_number
This is the model where the name is stored
alldata/models.py
class Salesman(models.Model):
slsmn_name = models.CharField(max_length=25)
id = models.IntegerField(db_column='number', primary_key=True)
I was reading I can add to the "attrs" in the widget an 'onchange' part, but I am not too familiar with how to approach this and tying it to the ajax request from forms and not the html.
forms.py
class WarehouseForm(AppsModelForm):
class Meta:
model = EmployeeWorkAreaLog
widgets = {
'employee_number': ForeignKeyRawIdWidget(EmployeeWorkAreaLog._meta.get_field('employee_number').remote_field, site, attrs={'id':'employee_number_field'}),
}
fields = ('employee_number', 'work_area', 'station_number')
views.py
class EnterExitArea(CreateView):
model = EmployeeWorkAreaLog
template_name = "operations/enter_exit_area.html"
form_class = WarehouseForm
def form_valid(self, form):
# do submission stuff..
def get_employee_name(request):
employee_number = request.GET.get('employee_number')
employee = Salesman.objects.get(id=employee_number)
employee_name = employee.slsmn_name
return employee_name
urls.py
urlpatterns = [
url(r'enter-exit-area/$', EnterExitArea.as_view(), name='enter_exit_area'),
path('ajax/get-employee-name/<int:id>/', views.get_employee_name, name='ajax_get_employee_name'),
]
The ajax request I tried to create is at the end of this html. I modified a similar request I found, but it does not actually display anything on the screen, not sure if I'm missing an area where the request is actually never being called, as I am not too familiar with how these types of requests work.
enter_exit_area.html
{% extends "base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" novalidate >
{% csrf_token %}
<div>
<h1 get-employee-name-url="{% url 'operations:ajax_get_employee_name' %}" id='employee_name'></h1>
<div>
{{ form.employee_number.help_text }}
{{ form.employee_number }}
</div>
<div>
{{ form.work_area.help_text }}
{{ form.work_area }}
</div>
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
</div>
</div>
</form>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$("#id_employee_number").change(function () {
var employee_number = $(this).val();
var url = $("#warehouseForm").attr("get-employee-name-url");
$.ajax({
url: url,
type:'GET',
data: {
'id': employee_number
},
success: function (data) {
var employee_name = data;
$('#employee_name')[0].innerHTML = employee_name;
}
});
});
</script>
The url definition for ajax_get_employee_name requires an id parameter:
path('ajax/get-employee-name/<int:id>/', views.get_employee_name, name='ajax_get_employee_name')
But the url call in the html file does not provide an id:
<h1 get-employee-name-url="{% url 'operations:ajax_get_employee_name' %}" id='employee_name'></h1>
Either update the url definition to not require an id, or update the url call to include one.
I am using wizard forms in django. I want to create a form field only if answer to some other form field is marked "yes" otherwise I don't want this new form field. How can I do this ?
I have tried some other answers related to this but most of them tells about how to mark field required or not but I want to display that field only if answer to other field is "Yes"
Django Form Wizard with Conditional Questions
In below code I want to display field "Pool2" only if answer to field "Pool" is marked "yes" otherwise I don't want that field. Basically I want to get some details of pool in field "Pool2" if there is pool in user's house.
forms.py
class ListingForm2(forms.Form):
Pool = (
("Yes","Yes"),
("No","No"),
)
Pool = forms.ChoiceField(choices = Pool,label = "Does your property have a pool ?")
Pool2 = forms.CharField(required=False)
Views.py
class ListingWizard(SessionWizardView):
template_name = 'listing_form.html'
form_list = [ListingForm1,ListingForm2,ListingForm3,ListingForm4]
def done(self, form_list, **kwargs):
save_data(form.cleaned_data for form in form_list)
return render(self.request,'done.html',{
'form_data' : [form.cleaned_data for form in form_list],
})
What you are trying to do have to be done with JavaScript, you could do it with only Django posts but it is not a right way.
Check this out:
class BookForm(forms.ModelForm):
has_sequel = forms.BooleanField(initial=True)
class Meta:
model = Book
fields = ['author', 'length', 'has_sequel', 'sequel']
class Media:
js = ('book_form.js', )
def clean(self):
if self.cleaned_data['has_sequel'] and self.cleaned_data['sequel'] is None:
raise ValidationError('You should indicate the sequel if the book has one.')
class BookView(FormView):
template_name = 'book_form.html'
form_class = BookForm
success_url = '/done/'
This code is including a Javascript with the form, this way you could reuse the form with its own Javascript, the Javascript code should be something like this (you maybe have to change javascript depending on how you print your form in the template):
$(document).ready(function() {
$('#id_has_sequel')[0].addEventListener('change', (event) => {
let sequelField = $('#id_sequel').parents('p');
if (event.target.checked) {
sequelField.show();
} else {
sequelField.hide();
}
})
});
And the template should be something like this:
{% load static %}
<head>
<title>Book form</title>
<script src="{% static 'jquery-3.4.1.min.js' %}"></script>
{{ form.media }}
</head>
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Send message">
</form>
If you have any question feel free to ask but trying to do this without Javascript it is not a good approach. As much you will find some kind of Django widget that will use Javascript too.
sorry this post may be messy not sure how do explain what I am looking for very well but here goes nothing.
I have a Django App and using django-table2 to print a data model to a table, the next thing I am looking to do it when the user clicks on the table row to redirect the page to a equivalent edit form
urls.py
path('', CustomerView.as_view(), name='customer'),
path('customer_edit/', views.customer_edit, name='customer_edit'),
tables.py
import django_tables2 as tables
from customer.models import Customer
class CustomerTable(tables.Table):
account = tables.Column(attrs={'td': {'class': 'account'}})
class Meta:
model = Customer
attrs = {'id': 'table'}
exclude = ('is_deleted',)
template_name = 'django_tables2/bootstrap-responsive.html'
views.py
from django.shortcuts import render
from django_tables2 import RequestConfig
from customer.models import Customer
from customer.tables import CustomerTable
from django.views.generic import TemplateView
class CustomerView(TemplateView):
template_name = 'customer/customer.html'
def get(self, request, *args, **kwargs):
table = CustomerTable(Customer.objects.all().filter(is_deleted=False))
RequestConfig(request).configure(table)
return render(request, 'customer/customer.html', {'table': table})
def customer_edit(request):
return render(request, 'customer/customer_edit.html')
template
{% extends 'base.html' %}
{% load render_table from django_tables2 %}
{% block head %}
<title>Dev Genie - Customers</title>
{% endblock %}
{% block body %}
<div class="input-group col-md-6">
<input type="button" class="btn btn-success" value="Add">
<input type="button" class="btn btn-danger" value="Delete">
<input class="form-control py-2" type="search" value="search" id="example-search-input">
<span class="input-group-append">
<button class="btn btn-outline-secondary" type="button">
<span class="glyphicon glyphicon-search"></span>
</button>
</span>
</div>
{% render_table table %}
<script>
$(document).ready(function () {
$('table:first').children('tbody:first').children('tr:first').css('background-color', '#0099ff');
$('table tbody tr').bind("mouseover", function () {
var colour = $(this).css("background-color");
$(this).css("background", '#0099ff');
$(this).bind("mouseout", function () {
$(this).css("background", colour);
});
});
$('table tbody tr').click(function () {
let account = $(this).closest('tr').find('td.account').text();
alert(account);
//on table row click event, pass back to django
});
});
</script>
{% endblock %}
I am struggling to get the account code from the onclick even to pass the account code back to Django to move to the next page to begin editing the record
I really think I am barking up the wrong tree with this
any help would be very much appreciated
I couldn't find any solution that suits my needs.
All the solutions I found requires some weird processing in Javascript and parsing slugs and PK's from the table to redirect to the correct URL.
My solution?
Define an absolute URL in your models.py
def get_absolute_url(self):
return reverse('product:detail', kwargs={'slug': self.slug})
Then in your tables.py, we add a data-href attribute to each column that we want to be clickable. This allows us to restrict which columns become clickable.
class ProductTable(tables.Table):
clickable = {'td': {'data-href': lambda record: record.get_absolute_url}}
name = tables.Column(attrs=clickable)
in_stock = tables.Column(attrs=clickable)
class Meta:
model = Product
fields = (name, in_stock)
And in your template just add this simple event listener,
$(document).ready(function($) {
$("td").click(function() {
window.location = $(this).data("href");
});
});
Alternatively, if you just want the whole row to be clickable, just use Row attributes as defined in the docs,
class ProductTable(tables.Table):
class Meta:
model = Product
row_attrs = {'data-href': lambda record: record.get_absolute_url}
fields = (name, in_stock)
and then change your template script too
$(document).ready(function($) {
$("tr").click(function() {
window.location = $(this).data("href");
});
});
I think i may have found a implementation for the above.
Putting a click event for a dialogue box with Django Tables2
it is for deleting a row but the concept is the same
I will test and check
Simple code to do that on row click or col
row_attrs = {
"onClick": lambda record: "document.location.href='/app/view/{0}';".format(record.id)
}
if you want to use it on col use tables.Column
Docs
Ok after spending this evening on this, I have found a way to perform this action without adding the href tag into the python code,
by using Ajax I can get the account code from the table and then pass this through to the url
$('table tbody tr').click(function () {
let account = $(this).closest('tr').find('td.account').text();
window.location = account;
});
adding the primary key to the url.py
path('<slug:account>/', views.customer_edit, name='customer_edit'),
and adding the customer_edit def to the views.py
def customer_edit(request, account):
customer = get_object_or_404(Customer, pk=account)
if request.method == 'POST':
form = CustomerEdit(request.POST, instance=customer)
if form.is_valid():
customer.save()
return redirect(reverse('customer:customer'))
else:
form = CustomerEdit(instance=customer)
args = {'customer': customer, 'form': form}
return render(request, 'customer/customer_edit.html', args)
this is the most optimum way I could find to redirect to another view from Django without having the url specified inside of the python file, I am 100% that there is better ways to do this but for now this will be the accepted answer
I may be a little confused about what you are trying to do. It seems like you are for some reason trying to have the view render a new response back from the click events on the table. That is why you are getting tripped up with all this JavaScript rendering. You should simply have those cells render as links that go to where you need them to.
Take a look at the django-tables2 documentation for TemplateColumn. You will want to just have it point to a template that creates the url given the record pk.
https://django-tables2.readthedocs.io/en/latest/pages/api-reference.html?highlight=templatecolumn#templatecolumn
tables.py
class CustomerTable(tables.Table):
account = tables.TemplateColumn(template_name="_account.html")
def render_title(self, record, value, column, bound_column, bound_row):
value = self.value_title(record, value)
return mark_safe( # noqa: S308, S703
column.render(record, self, value, bound_column, bound_row=bound_row)
)
_account.html
<a href={% url('customer_edit', args=[record.pk]) %}>your text here</a>