What I'm trying to do is a 2 tier search with drop down menus using Select widget, the results will be a listing of the fields from my Meta.model. the first Tier is a a State listing from State.model. Upon a select it is supposed to list out all of the cities with in the selected state, the problem I'm having (and I think its due to my lack of knowledge) is that the city listing is not filtered but a listing of all cities in my database regardless of state. I'm not sure where or how to pass my variable to be able invoke my .filter() statement.
models.py
class Meta(models.Model):
rcabbr = models.CharField(max_length = 15)
slug = models.SlugField(unique=False)
state = models.ForeignKey('State')
rc_state = models.CharField(max_length = 3)
oerp = models.CharField(max_length=18)
subgrp = models.SlugField()
sonus_pic = models.CharField(max_length=8)
ems = models.CharField(max_length=14)
agc = models.CharField(max_length=14)
def __unicode__(self):
return self.rcabbr
class State(models.Model):
name = models.CharField(max_length=2)
slug = models.SlugField(unique=True)
state_long = models.CharField(max_length=15)
owning_site = models.CharField(max_length=12)
def __unicode__(self):
return self.name
return self.state_long
forms.py
class states(forms.Form):
invent = [(k.name,k.state_long) for k in State.objects.all()]
rclist = forms.ChoiceField(widget=forms.Select, choices=invent)
class rateCenter(forms.Form):
invention = [(k.id,k.rcabbr,k.rc_state) for k in Meta.objects.all()]
rcviews = forms.ChoiceField(widget=forms.Select, choices=invention)
views.py
def StateAll(request):
""" This lists out all of the states within the database that
are served"""
rclist = states()
return render(request, 'statelist.html',{'rclist': rclist})
def RcView(request):
""" this should list all the rateCenters within
the state that was selected in StateAll() """
rclist = request.GET['rclist']
forms = rateCenter()
return render(request, 'rclist.html',{'forms': forms})
Logic tells me I should to do my .filter() statement in the forms.py but unsure how to pass the result from the request.GET in StateAll() view. I do have the debug_toolbar installed so I can see the variable u'rclist' and the value u'LA' (my test state). I had this working 100% using hyperlinks however the size of my test database is miniscule in comparison to what is going to be in the production version and HREF's are just not possible.
my understanding is:
ChainedForeignKey(LinkedModel, LinkedModel.field = "field in first Tier", chained_model_field = "current model_field")
so simple model should I think be something like this?
def State(model.models):
name = models.CharField(max_length=20) #this is the state abbreviation
state_long = models.CharFeild(max_length=20)#this is state long form
def Meta(model.models):
state = models.CharField(max_length=20)
slug = models.SlugField(unique = False) #same values as rcabbr
rcabbr = ChainedForeignKey(State, chained_field = "state_long",
chained_model_field = "slug")
.....
Does that look about right........so the First Field in the drop down should be the State_long, once selected the next should be the slug?. at which time the slug should be passed to my urls and the views for the that final page.
I am going to try this however I'm not 100% sure how to do my views and if I need to do something with forms page or does this cover it? The documentation is not user friendly for someone new to this so any input would be most appreciated!
There are many third party libraries django-smart-selects and dajax come to mind - that will automate this for you along with provide you the necessary javascript to filter the form fields on the fly.
If you are investigating those, here is how you would do it with just the django forms:
class RateCenterForm(forms.Form):
rate_center = forms.ModelChoiceField(queryset=Meta.objects.all())
def __init__(self, *args, **kwargs):
state = kwargs.pop('state')
super(RaterCenterForm, self).__init__(*args, **kwargs)
self.fields['rate_center'].queryset = Meta.objects.filter(state=state)
A ModelChoiceField is a select drop down that takes its values from a model.
Now in your view, you would call it like this:
def show_rate_centers(request):
form = RateCenterForm(state='SomeState')
# .. your normal logic here
Related
I have the following doubt:
Let's assume that my Django project has the following models:
class State(models.Model):
initials = models.CharField('Initials', max_length=2, blank = False)
name = models.CharField('State', max_length=50, blank = False)
count = models.IntegerField('Foo Counter', default=0)
....
class Foo(models.Model):
name = models.CharField('Name', max_length=50, blank = False)
state = models.ForeignKey(State, verbose_name='State', related_name='state_fk', on_delete=models.DO_NOTHING),
....
So i have a form to add Foo instances to my db:
class FooForm(forms.ModelForm):
class Meta:
model = Foo
fields = '__all__'
this is the view.py file:
def home(request):
template_name = 'home.html'
form = FooForm(request.POST or None)
if form.is_valid():
salvar = form.save(commit=False)
salvar.save()
return redirect('FooApp:home')
else:
context = {
'form': form
}
return render(request, template_name, context)
I need that, every time the user registers a new 'Foo' the counter of the 'State' chosen by him is increased by 1, i search a lot here and in docs of Django but i could not find a way to do this.
Why do you need count defined as a model field if it's dependent on a database computation, and not something that will be entered from outside?
As mentioned before, you can add logic in the application to update count value from State to self.foo_set.count()
However, I think that it might worth looking into a different approach which would be defining a cached_property on State as it follows:
#cached_property
def count(self):
return self.foo_set.count()
In this way, you'll be able to access State.count wherever you want in the application and get the right value without worrying to keep it updated.
You may not need to keep track of count manually like that. For any instance of State you can always call:
state.foo_set.count()
That will always give you the current count.
What is the best way to limit only one record to be default in django
I have a model where i have a flag for default
class BOMVersion(models.Model):
name = models.CharField(max_length=200,null=True, blank=True)
material = models.ForeignKey(Material)
is_default = models.BooleanField(default=False)
I want to have only one value to be default for the same material but this material can have a lot of non default ones.
It was odd that this question was not addressed more often. If I have a default record, I want to record that in the class as a member variable. And to determine if an instance is the default, I want to compare the class default member variable with the instance id. Unfortunately I could not figure out how to access class variables and instance variables nicely in the same class function in Python (may be someone can comment), but this does not require to hit the database for a default or store a bunch of records pointing to a default. Just one member variable in the class.
After I wrote this, I realized every time the application is restarted, default is reset to None, so you will have to store this in a database. I have updated my answer accordingly. However, checking that the member variable is not null, and only hitting the database if it is would reduce hits here. The model I used was:
class RecordOfInterest(models.Model):
"""
Record Records of interest here. Stores ID, and identifying character
"""
# asume maximum 64 character limit for the model.
model_name = models.CharField(max_length=64, unique=True)
record_no = models.IntegerField()
human_ident = models.CharField(max_length=64, help_text='How is this of interest')
# Id it as default, deposit, ... Don't bother indexing, as there will only be a few
def __unicode__(self):
return u'Model %s record %d for %s' % (self.model_name, self.record_no, self.human_ident)
class Meta:
unique_together = ('model_name', 'human_ident')
class Product(models.Model):
"""
Allow one product record to be the default using "Product.default = prod_instance"
check default with "Product.is_default(prod_instance)"
"""
default = None # set this to id of the default record
cart_heading = models.CharField(max_length=64, blank=True)
country = CountryField()
pricing = models.ForeignKey(
'Price', blank=True, null=True, related_name='visas', on_delete=models.SET_NULL)
#classmethod
def is_default(cls, obj):
if cls.default_no == None:
try:
cls.default_no = RecordOfInterest.objects.get(model_name=cls.__name__, human_ident='default')
except RecordOfInterest.DoesNotExist:
default_no = None
return cls.default_no == obj.id
#classmethod
def set_default(cls, obj):
try:
default_rec = RecordOfInterest.objects.get(model_name=cls.__name__, human_ident='default')
except RecordOfInterest.DoesNotExist:
RecordOfInterest.objects.create(model_name=cls.__name__, record_no=obj.id, human_ident='default')
else:
if default_rec.record_no != obj.id:
default_rec.record_no = obj.id
default_rec.save()
cls.default_no = obj.id
return
Saving the ID in settings.py if it is static.
Save it into a separate "default" table with one record (or use the most recent) if it's dynamic.
Save the default in another table like this:
class BOMVersion(models.Model):
name = models.CharField(max_length=200,null=True, blank=True)
material = models.ForeignKey(Material)
class BOMVersionDefault(model.Models)
time_set= models.Datetime(auto_created=True)
default_material = models.ForiegnKey(Material)
To query:
default = BOMVerDefault.objects.latest(time_set).get().default_material
If you have several material types that each need a default then default_material would be a field in a material-type table.
Getting one record to be default in a table is most basic requirement we developer come face to face, after spending couple of hours over it, i think a neat and clean solution in django would be update all records to default false if current form instance has default value to be "true" and then save the record.
class FeeLevelRate(TimeStampedModel):
"""
Stores the all the fee rates depend on feelevel
"""
feelevel = models.ForeignKey(FeeLevel, on_delete= models.PROTECT)
firstconsultfee = models.DecimalField(_('First Consultation Charges'),max_digits=10,decimal_places=2,blank=True)
medcharges = models.DecimalField(_('Medicines Charges per Day'),max_digits=10,decimal_places=2,blank=True)
startdate = models.DateField(_("Start Date "), default=datetime.date.today)
default_level = models.BooleanField(_('Is Default Level?'),default=False)
class Meta:
constraints = [
models.UniqueConstraint(fields=["feelevel","startdate"], name='unique_level_per_date'),
]
def __str__(self):
return "%s applicable from (%s)" % ( self.feelevel, self.startdate.strftime("%d/%m/%Y"))
class FeeLevelRateCreate(CreateView):
model = FeeLevelRate
fields = ['feelevel', 'firstconsultfee', 'medcharges', 'startdate', 'default_level']
context_object_name = 'categories'
success_url = reverse_lazy('patadd:feerate_list')
def form_valid(self, form):
# Update all the default_level with false.
#UserAddress.objects.filter(sendcard=True).update(sendcard=False)
if form.instance.default_level:
FeeLevelRate.objects.filter(default_level=True).update(default_level=False)
return super().form_valid(form)
I am new to Django and for learning purposes I am trying to build my own site using the linkedn API to display my profile. The following is a a example of my code. To see the whole lot:
https://github.com/javiee/django-site
models.py
class Profile(models.Model):
user = models.ForeignKey(User)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
def __str__(self):
return self.first_name, self.last_name
class Education(models.Model):
user = models.ForeignKey(User)
school_name = models.CharField(max_length=100)
field_study = models.CharField(max_length=100)
degree = models.CharField(max_length=100)
start_date = models.CharField(max_length=20)
end_date = models.CharField(max_length=20)
and views.py
profile = Profile(first_name = content['firstName'],
last_name = content['lastName'],
user = request.user)
profile.save()
#Education model
content_educ = content['educations']['values']
for value in content_educ:
education = Education(school_name = value['schoolName'],
user = request.user,
field_study = value['fieldOfStudy'],
degree = value['degree'],
start_date = value['startDate']['year'] ,
end_date = value['endDate']['year'])
education.save()
This all working but my problem is that everytime I check linkedn, the code saves all the objects again. What it would ideally do is to "update" fields based on the profile when the .save() method is called. I have read the next link https://docs.djangoproject.com/en/1.7/ref/models/instances/#saving-objects
but I dont manage to get it working, perhaps foreigns keys are not properly set so any advise/help/tip will be much appreciated. Thanks!
Use the update_or_create() method:
Education.objects.update_or_create(
school_name=value['schoolName'],
user = request.user,
defaults={'field_study': value['fieldOfStudy'],
'degree': value['degree'],
'start_date': value['startDate']['year'] ,
'end_date': value['endDate']['year']})
The problem you're having is that you're instantiating new Education instances in these lines:
education = Education(school_name = value['schoolName'],
user = request.user,
field_study = value['fieldOfStudy'],
degree = value['degree'],
start_date = value['startDate']['year'] ,
end_date = value['endDate']['year'])
When Django goes and tries to save these new instances (instances for which id is not yet defined), Django goes ahead and inserts the records rather than doing the update you want.
To do an update, you can either try to get the record, catching the DoesNotExist exception:
try:
education = Education.objects.get(school_name=value['schoolName'],
user=request.user,
field_study=value['fieldOfStudy'],
degree=value['degree'],
start_date=value['startDate']['year'],
end_date=value['endDate']['year'])
except Education.DoesNotExist:
education = Education(school_name=value['schoolName'],
user=request.user,
field_study=value['fieldOfStudy'],
degree=value['degree'],
start_date=value['startDate']['year'],
end_date=value['endDate']['year'])
then apply whatever updates you want/need.
Or you can use get_or_create to do the same:
(education, created) = Education.objects.get_or_create(school_name=value['schoolName'],
user=request.user,
field_study=value['fieldOfStudy'],
degree=value['degree'],
start_date=value['startDate']['year'],
end_date=value['endDate']['year'])
If you don't want to look up your instances by all of those values (they're AND-ed), but want to initialize new instances with certain values, you should look up the defaults keyword for get_or_create.
Or you can use update_or_create as suggested by catavaran.
edit: Or, if you just want to do a straight update of a record without getting it (this also works with multiple objects at once), you can use queryset.update
Education.objects.filter(attribute=value, ...).update(attribute2=value2, ...)
I think what I need to do is really the opposite of an inlineformset.
Say I have:
from django.db import models
class Type(models.Model):
description = models.CharField(max_length=50)
status = models.ForeignKey(Status)
def __unicode__(self):
return self.description
class ColorType(models.Model):
type = models.ForeignKey(Type)
color = models.ForeignKey('Color')
status = models.ForeignKey(Status)
def __unicode__(self):
return u'%s %s' % (self.type, self.color)
class Color(models.Model):
description = models.CharField(max_length=50)
status = models.ForeignKey(Status)
types = models.ManyToManyField(type, through=ColorType)
def __unicode__(self):
return self.description
class Chair(models.Model):
description = models.CharField(max_length=50)
status = models.ForeignKey(Status)
colorType = models.ForeignKey(ColorType)
Now I need a form to edit a chair in which I enter Color and Type separatedly (Like showing a modelformset of ColorType). If the combination doesn't exist the application has to create the necessary instance of ColorType (and assigning it a desfault status) and assign it to the chair.
I think the whole situation is common, I should be missing something...
I did my search and, sadly, it's not currently possible to have more than one model per form using only Django. This being said, you are not the only one who wanted to do that.
There's a SO answer here which make good suggestions.
If you still want to to it, you can roll your own using something like this, but a little different because this version accepts one form or the other, not both at once.
If you don't want to code it yourself, django-multiform is a Django library that offers a generic way to do what you want. Of course, you would need to change the save method to apply for your use-case (ColorType combination, etc.)
I will post my own solution. Finally I did the trick just by using the InlineFormsets.
In forms.py:
class ChairForm(forms.ModelForm):
class Meta:
model = Chair
exclude = ('colorType')
class ColorTypeForm(forms.ModelForm):
class Meta:
model = ColorType
exclude = ('status')
In views.py:
def ChairUpdate(request, chair_id):
chair = get_object_or_404(Chair, pk=chair_id)
form = ChairForm(instance=chair)
ColorTypeInlineFormset = inlineformset_factory(ColorType, Chair, formset=ColorTypeForm)
if request.method == 'POST':
form = ChairForm(request.POST, instance=chair)
if form.is_valid():
chair = form.save(commit=False)
colorTypeInlineFormset = ColorTypeInlineFormset(request.POST,)
# colorTypeInlineFormset.is_valid()
color = Color.objects.get(id=request.POST['color'])
type = Type.objects.get(id=request.POST['type'])
ct,created = ColorType.objects.get_or_create(color=color,type=type,defaults={'status':Status.objects.get(id=1)})
chair.colorType = ct
# marcaModeloInlineFormset.save(commit=False)
arma.save()
return HttpResponseRedirect(reverse('success_page'))
colorTypeInlineFormset = ColorTypeInlineFormset(instance=chair.colorType)
return render(request, "chair_form.html", {
'chair': chair,
'form': form,
'colorType_formset': colorTypeInlineFormset,
'action': "Update"
})
I have an application to count the number of access to an object for each website in a same database.
class SimpleHit(models.Model):
"""
Hit is the hit counter of a given object
"""
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
site = models.ForeignKey(Site)
hits_total = models.PositiveIntegerField(default=0, blank=True)
[...]
class SimpleHitManager(models.Manager):
def get_query_set(self):
print self.model._meta.fields
qset = super(SimpleHitManager, self).get_query_set()
qset = qset.filter(hits__site=settings.SITE_ID)
return qset
class SimpleHitBase(models.Model):
hits = generic.GenericRelation(SimpleHit)
objects = SimpleHitManager()
_hits = None
def _db_get_hits(self, only=None):
if self._hits == None:
try:
self._hits = self.hits.get(site=settings.SITE_ID)
except SimpleHit.DoesNotExist:
self._hits = SimpleHit()
return self._hits
#property
def hits_total(self):
return self._db_get_hits().hits_total
[...]
class Meta:
abstract = True
And I have a model like:
class Model(SimpleHitBase):
name = models.CharField(max_length=255)
url = models.CharField(max_length=255)
rss = models.CharField(max_length=255)
creation = AutoNowAddDateTimeField()
update = AutoNowDateTimeField()
So, my problem is this one: when I call Model.objects.all(), I would like to have one request for the SQL (not two). In this case: one for Model in order to have information and one for the hits in order to have the counter (hits_total). This is because I cannot call directly hits.hits_total (due to SITE_ID?). I have tried select_related, but it seems to do not work...
Question:
- How can I add column automatically like (SELECT hits.hits_total, model.* FROM [...]) to the queryset?
- Or use a functional select_related with my models?
I want this model could be plugable on all other existing model.
I have finaly find the answer, I have changed my manager and now the columns will be add to the db request.
select = {
'hits': 'hits_simplehit.hits_total',
}
qset = super(SimpleHitManager, self).get_query_set()
qset = qset.extra(select=select)
qset = qset.filter(hits_rel__site=settings.SITE_ID)
return qset
Thank you :)
Have you considered the performance impact of doing even a single database hit per object?
If you have a small amount of Objects, keep the whole table in memory and send off disk-writes as an asynchronous (background) task.