Django: How to create Table and then update Database through them - python

I am new at Django and I have tried my best to understand it but I still have a long way to go.
I am working on a project in which I have to make a webpage where I have to display data from these classes :
class team(models.Model):
team_name = models.CharField(max_length = 40)
def __unicode__(self):
return self.team_name
class metric(models.Model):
team = models.ForeignKey(team)
metric_name = models.CharField(max_length = 40)
def __unicode__(self):
return self.metric_name
class members(models.Model):
metric = models.ForeignKey(metric)
member_ID = models.CharField(max_length = 40)
member_name = models.CharField(max_length = 40, null=True, blank=True)
def __unicode__(self):
return self.member_ID
Furthermore I want this data to be editable as well. Meaning there should be an Edit button which once pressed would enable the user to edit these fields and once he saves it the database get's updated automatically.
Now as far as I have understood. I would need to display this data using tables of some sort and then once the user clicks on the Edit button I would have to change the Tables into form so he can edit the data ? and then update the database once he clicks on save.
I am not sure how to do all this in Django.
If anyone could please refer to me step by step on how to proceed and maybe refer to some References It would help me a lot
Thanks :)
UPDATE: I forgot to mention it before. I have already created a page where user selects team_name.. Then he is redirected to another page where I want to show the table for that specific team. And I also want that page to be editable. If I use modelforms then I won't be able to access single object of my team model.
UPDATE2: I am still stuck at this. I can't understand on how to display just few elements and variables of a model class rather then all of it. Apart from that I can't understand how to create a Form that can take input from the user on which object of the database to edit and then display that certain objects to be editable.

Related to the questioneers-comments here and here.
First of all, you should write your class-names capitalized. Please check the documentation.
You create Forms for a queryset like this.
forms.py
from django.forms import ModelForm
class MembersForm(ModelForm):
class Meta:
model = Members
urls.py
from django.conf.urls.defaults import *
urlpatterns = patterns('yourapp.views',
url(r'^$',
# /
view = 'index',
name = 'team-index',
),
url(r'^(?P<lookup_team>[-\w]+)/$',
# .../team-extreme/
view = 'update',
name = 'update-members',
),
)
views.py (of your application)
from django.http import HttpResponse
from django.template.context import RequestContext
from django.shortcuts import render_to_response, reverse, redirect
from yourapp.models import Members
from yourapp.forms import MembersForm
def index(request):
queryset = Members.objects.all()
template = 'yourapp/index.html'
kwvars = {
'data': queryset,
}
return render_to_response(template, kwvars, RequestContext(request))
def update(request, lookup_team):
"""change members being in a special team"""
queryset = Members.objects.filter(metric__team__team_name=lookup_team)
if request.POST:
form = MemberForm(request.POST, instance=queryset)
if form.is_valid():
form.save()
return redirect(reverse('team-index'))
else:
form = MemberForm(instance=queryset)
template = 'yourapp/update.html'
kwvars = {
'form': form,
}
return render_to_response(template, kwvars, RequestContext(request))
Hope this helps. If you got any questions hit me a comment.

If you want to implement it as dynamically as you describe, then this is impossible by Django alone; Django can serve you the table and the forms, but a dynamic change without reloading the page (which I'm guessing is what you really want) needs some client side code, JavaScript.
But about showing the data and the forms: use Django's class based generic views.
The ListView and DetailsView should get you started with showing the data from the models.
The ModelForm in combination with the appropriate generic editing views should get you going with editing the data.
These views will, per default, return the data in some - generally useful - format and can be customized by using your own template. I suggest you read up on the links I provided you and work from there. That should get you going.

Related

Limiting choices in foreign key dropdown in Django using Generic Views ( CreateView )

I've two models:
First one:
class A(models.Model):
a_user = models.ForeignKey(User, unique=False, on_delete=models.CASCADE)
a_title = models.CharField("A title", max_length=500)
Second one:
class B(models.Model):
b_a = models.ForeignKey(A, verbose_name=('A'), unique=False, on_delete=models.CASCADE)
b_details = models.TextField()
Now, I'm using CreateView to create form for Value filling :
class B_Create(CreateView):
model = B
fields = ['b_a','b_details']
Then using this to render these field in templates.
Now, my problem is, while giving the field b_a ( which is the dropdown ), it list downs all the values of model A, but the need is to list only the values of model A which belongs to the particular logged in user, in the dropdown.
I've seen all the answers, but still not able to solve the problem.
The things I've tried:
limit_choices_to in models : Not able to pass the value of A in the limit_choices
form_valid : Don't have the model A in the CreateView, as only B is reffered model in B_Create
passing primary key of A in templates via url : Then there is no instance of A in the template so can't access. Also, don't want to handle it in templates.
I'm new to Django and still learning, so don't know to override admin form.
Please suggest the implemented way, if possible to the problem. I've researched and tried most of the similar questions with no result for my particular problem. I feel like, this is a dumb question to ask, but I'm stuck here, so need help.
Thanks..
(Please feel free to suggest corrections.)
You have access to self.request.user in the form_valid of the view. But in order to limit the choices in the form you have to customize the form before it is served initially. You best override the view's get_form and set the form field's queryset:
class B_Create(CreateView):
model = B
fields = ['b_a','b_details']
def get_form(self, *args, **kwargs):
form = super(B_Create, self).get_form(*args, **kwargs)
form.fields['b_a'].queryset = self.request.user.a_set.all()
# form.fields['b_a'].queryset = A.objects.filter(a_user=self.request.user)
return form
Generally, there are three places where you can influence the choices of a ModelChoiceField:
If the choices need no runtime knowledge of your data, user, or form instance, and are the same in every context where a modelform might be used, you can set limit_choices_to on the ForeignKey field itself; as module level code, this is evaluated once at module import time. The according query will be built and executed every time a form is rendered.
If the choices need no runtime knowledge, but might be different in different forms, you can use custom ModelForms and set the queryset in the field definition of the respective form field.
If the queryset needs any runtime information, you can either override the __init__ of a custom form and pass it any information it needs to set the field's queryset or you just modify the queryset on the form after it is created which often is a quicker fix and django's default views provide nice hooks to do that (see the code above).
The #schwobaseggl answer is excellent.
Here is a Python 3 version. I needed to limit the projects dropdown input based on the logged-in user.
class ProductCreateView(LoginRequiredMixin, CreateView):
model = Product
template_name = 'brand/product-create.html'
fields = '__all__'
def get_form(self, form_class=None):
form = super().get_form(form_class=None)
form.fields['project'].queryset = form.fields['project'].queryset.filter(owner_id=self.request.user.id)
return form

Set value of excluded field in django ModelForm programmatically

I have a ModelForm class where I want to exclude one of the fields (language) from the form and add it programmatically. I tried to add the language in the clean method, but got an IntegrityError ("Column 'language_id' cannot be null"). I assume this is because any data for language returned from clean() will be ignored since it's in the exclude tuple. I'm using the form in a ModelAdmin class. I haven't managed to find any way to deal with this, so any tips or hints will be much appreciated.
from django import forms
from myapp import models
from django.contrib import admin
class DefaultLanguagePageForm(forms.ModelForm):
def clean(self):
cleaned_data = super(DefaultLanguagePageForm, self).clean()
english = models.Language.objects.get(code="en")
cleaned_data['language'] = english
return cleaned_data
class Meta:
model = models.Page
exclude = ("language",)
class PageAdmin(admin.ModelAdmin):
form = DefaultLanguagePageForm
What you can do is when you save the form in the view, you don't commit the data. You can then set the value of the excluded field programmatically. I run into this a lot when I have a user field and I want to exclude that field and set to the current user.
form = form.save(commit=False)
form.language = "en"
form.save()
Here's a little more info on the commit=False --> https://docs.djangoproject.com/en/1.6/topics/forms/modelforms/#the-save-method

Retrieving a ForeignKey attribute based on the URL of the website?

I am trying to create a function that allows individuals to post something on an associated userpage. I have created both the model ‘newpost’ and ‘newpostform’ (below). I am having trouble writing the view function to look at the current URL of the page and then take that parameter and attach it to the newpost model’s ForeignKey field automatically. For example, if I am at the URL myapp.com/userpage1 and I click on the “post” button on that page, I want to create a newpost object which automatically has the ForeignKey field filled in as ‘userpage1’. Basically, I am trying to create an app where people can easily navigate userpages by entering the userpage parameter into the URL and easily make posts on those pages quickly and concisely - kind of like how reddit's subreddit system works by entering the name of the subreddit into the URL bar. Thanks for any help and hints.
model:
class newpost(models.Model):
newlinktag = models.ForeignKey(‘userpage’) #tags link to which userpage the post belongs to
postcontent = models.CharField(max_length=1024)
postdate = models.DateTimeField(auto_now_add=True) #submission timestamp.
postlikes = models.IntegerField(null=False, default=0)
def __unicode__(self):
return self.postcontent
form:
class newpostform(forms.ModelForm):
postcontentform = models.CharField(max_length=1024)
class Meta:
model = newpost
urls.py:
url(r'^(?P<url_user_id>[\w\-]+)/$', your_view)
views.py
def your_view(request, url_user_id)
# you have the foreign key in the url_user_id field.
...
if request.POST:
new_post_with_foreign_key = newpost(newlinktag=url_user_id, ...)
...
new_post_with_foreign_key.save()
Don't do this in the form. Exclude the FK field from the modelform altogether, and set it in the view on save.
if form.is_valid():
post = form.save(commit=False)
post.newlinktag = page
post.save()
(You might like to consider following PEP8 and using some CapitalLetters in your class names, and underscore_names in your field names. It'll make your code much easier to read.)

Filtering cities by country in the django admin edit form

I'm using django-cities on a small project that lists locations.
The locations model is this:
class Location(models.Model):
def __unicode__(self):
return self.name
name = models.CharField(max_length=200)
instagram_id = models.IntegerField(default=0)
country = models.CharField(max_length=200)
city = models.CharField(max_length=200)
new_city = models.ForeignKey(City, related_name='venuecity', null=True, blank=True)
latitude = models.DecimalField(max_digits=17, decimal_places=14, default=0)
longitude = models.DecimalField(max_digits=17, decimal_places=14, default=0)
the 'city' and 'new_city' fields are temporary while I migrate the data.
My problem is on the admin side of things, how can I filter the city select field by country? The goal is to make it easier for the user to select the correct city when he is adding a new record or editing an existing one.
I looked through django's admin documentation but couldn't figure out how to do this.
You can customize the form used by the Django admin. In your app, create a form that exhibits the behavior you want. In this case you will probably want to write one that overloads the form's __init__() method and adjusts the values of your city field dynamically. E.g.:
class LocationAdminForm(ModelForm):
def __init__(self, *args, **kwargs):
super(LocationAdminForm, self).__init__(*args, **kwargs)
if self.instance:
# we're operating on an existing object, not a new one...
country = self.instance.country
cities = # get cities from your master countries list...
self.fields["new_city"] = ChoiceField(choices=cities)
Once you have a working ModelForm, you can tell the admin to use it in your admin.py:
class LocationAdmin(admin.ModelAdmin):
form = LocationAdminForm
The admin customization is documented here. An old but still mostly relevant article on dynamic Django forms is here.
To do this for NEW records, you will need to implement most of it on the front-end. You will need Javascript code that retrieves and updates the cities list dynamically as a different country is selected. Your Django app would expose an AJAX view the Javascript can call. It would pass the country and return a simple JSON object of cities, which it would use to update the element in the form.
Alternatively, a pure server-side solution is possible if you change the workflow. This could work like a "wizard" and ask the user to first select the country. Click next to view step 2 of the wizard with a dynamically generated Django form as above. The Django wizard app may be of use.
I tried the above solution (#jdl2003) but it didn't work (at least in Django 1.8) and I was getting the same error as #bruno-amaral mentioned. I was able to fix the issue like this:
#forms.py
from django.forms import ModelForm, ModelChoiceField
from cities_light.models import City
class MyUserAdminForm(ModelForm):
def __init__(self, *args, **kwargs):
super(MyUserAdminForm, self).__init__(*args, **kwargs)
if self.instance:
country = self.instance.country
cities = country.city_set.all() if country else City.objects.none()
self.fields['city'] = ModelChoiceField(queryset=cities)
#admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from myapp.forms import MyUserAdminForm
class MyUserAdmin(UserAdmin):
form = MyUserAdminForm
UserAdmin.fieldsets += (
(None, {'fields': ('dob', 'country', 'city')}),
)
UserAdmin.list_display += ('country',)
admin.site.register(get_user_model(), MyUserAdmin)
Only issue I have now, is that cities list is not getting updated dynamically. I need to first save the country field and after that cities are updated.

django-admin - how to modify ModelAdmin to create multiple objects at once?

let's assume that I have very basic model
class Message(models.Model):
msg = models.CharField(max_length=30)
this model is registered with admin module:
class MessageAdmin(admin.ModelAdmin):
pass
admin.site.register(Message, MessageAdmin)
Currently when I go into the admin interface, after clicking "Add message" I have only one form where I can enter the msg.
I would like to have multiple forms (formset perhaps) on the "Add page" so I can create multiple messages at once. It's really annoying having to click "Save and add another" every single time.
Ideally I would like to achieve something like InlineModelAdmin but it turns out that you can use it only for the models that are related to the object which is edited.
What would you recommend to use to resolve this problem?
This may not be exactly what you are looking for, but if you want to create multiple objects at the same time you could to somehthing like this:
#In /forms.py
MessageAdminForm(forms.ModelForm):
msg = CharField(max_length=30)
count = IntegerField()
#In /admin.py
from app.admin import MessageAdminForm
MessageAdmin(admin.ModelAdmin):
form = MessageAdminForm
fieldsets = (
(None, {
'fields' : ('msg','count')
}),)
def save_model(self, request, obj, form, change):
obj.msg = form.cleaned_data['msg']
obj.save()
for messages in range(form.cleaned_data['count']):
message = Message(msg=form.cleaned_data['msg'])
message.save()
Basicly what you are doing is creating a custom form for your admin template, which ask the user how many times the object shall be created. The logic is than interpreted in the save_model method.
As a workaround, Since, It is likely that you have a FK to User, so you could define an InlineModel on the User model.
Otherwise, the easiest approach may be to create a custom admin view since, there isn't a generic admin view that displays and saves formsets.
This is easy if you are using an Inline. Then you could use extra = 10 or however many extra formsets you want. There doesn't seem to be an equivalent for the ModelAdmin.
Of course in your messages model you would need to create a ForeignKey to some sort of message grouping model as another layer of function and to get the multi-formset layout that you are looking for.
For example:
models.py:
class Group(models.Model):
name = models.CharField(max_length=30)
class Message(models.Model):
msg = models.CharField(max_length=30)
grp = models.ForeignKey(Group)
admin.py:
class MessageInline(admin.TabularInline):
model = Message
extra = 10
class GroupAdmin(admin.ModelAdmin):
inlines = [MessageInline]
admin.site.register(Group, GroupAdmin)
This would give you what you want in the Admin view and create grouping (even if you only allow for one group) and the only extra field would be the name in the group model. I am not even sure you would need that. Also I am sure the value for extra could be generated dynamically for an arbitrary value.
I hope this helps!

Categories