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)
Related
So what I want to do is to set a limit of forms with the same value. There are different activities from a foreign model to which students can apply.
I have name(naam), student number(studentnummer), activity(activiteit) and class(klas), and I want to set a limit of 10 forms with the same activity (max of people who can do the same activity) and a limit of 1 for student number (so students can only apply for one activity).
models.py
class Klas(models.Model):
klas = models.CharField(max_length=8)
def __str__(self):
return f"{self.klas}"
class Activiteit(models.Model):
titel = CharField(max_length=64)
docent = CharField(max_length=32)
icon = models.ImageField()
uitleg = models.TextField()
def __str__(self):
return f"{self.id}:{self.titel}:{self.docent}"
class Aanmelden(models.Model):
naam = CharField(max_length=32)
studentnummer = IntegerField()
klas = ForeignKey(Klas, on_delete=models.CASCADE, default=None, blank=True)
activiteit = ForeignKey(Activiteit, on_delete=models.CASCADE, default=None, blank=True)
def __str__(self):
return f"{self.id}:{self.naam}:{self.studentnummer}"
views.py
def home(request):
activiteiten = Activiteit.objects.all()
form = AanmeldenForm()
if request.method == 'POST':
form = AanmeldenForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'home.html', {
'activiteiten': activiteiten,
'form':form,
})
forms.py
class AanmeldenForm(forms.ModelForm):
class Meta:
model = Aanmelden
fields = (
'naam','studentnummer','klas','activiteit'
)
If you know how to solve this or have a better idea of doing this kind of system please let me know.
Thanks in advance!!
You probably need to have a variable inside your Aktiviteit class, such as plaatsen_beschikbaar that is initialised at 10 when you create a new instance of an Aktiviteit. Then on successful Aanmelden to an instance you reduce it by one. You need to make sure in your model that plaatsen_beschikbaar cannot be less than zero, and if someone deletes the aanmelding then increase the variable by 1. Edit: or you use a validator such as the one below to restrict Aanmelden. You could also make your model have a #property that returns the plaatsen_beschikbaar instead of using a field. On second thought, this seems like a better plan, hence this edit
To make sure a student may only have 1 Aanmelden, you could just make your studentnummer unique in your Aanmelden class. But that makes your model not future-proof if you decide in future that students may subscribe to two or three activities. In that case you need to have a foreign key relationship to Student and restrict the number of Aanmelden that a student may have using a validator like so for instance
def beperk_aanmelden(value):
if Aanmelden.objects.filter(student_id=value).count() >= 1:
raise ValidationError('Student heeft al de maximale aanmeldingen')
else:
return value
Then in your Aanmelden model:
student = ForeignKey(Student, validators=[beperk_aanmelden,])
Edit: based on your current model it would look like this:
def beperk_aanmelden(value):
if Aanmelden.objects.filter(studentnummer=value).count() >= 1:
raise ValidationError('Student heeft al de maximale aanmeldingen')
else:
return value
And in your model:
studentnummer = IntegerField(validators=[beperk_aanmelden,])
Edit 2:
To check the plaatsen_beschikbaar you could do something like this:
def beperk_activiteit(value):
if Activiteit.objects.get(activiteit_id=value).plaatsen_beschikbaar <= 0:
raise ValidationError('Activiteit heeft geen plaatsen beschikbaar meer! Kies een andere activiteit.')
Then for the field in your model:
activiteit = ForeignKey(Activiteit, on_delete=models.CASCADE, default=None, blank=True, validators=[beperk_activiteit,])
Edit 3:
For the plaatsen_beschikbaar I would do something like this. Each Activiteit has a capaciteit where you set the maximum places available. Then define an #property, which gives you a non-db calculated field which you can access just like a normal field. The difference is that it's not stored, but re-calculated each time you access it. Inside count the number of Aanmelden that have the Activiteit instance as related object. That's your number of already booked placed. Then subtract this from capaciteit and you'll have a current plaatsen_beschikbaar
class Activiteit(models.Model):
titel = CharField(max_length=64)
docent = CharField(max_length=32)
icon = models.ImageField()
uitleg = models.TextField()
capaciteit = models.IntegerField()
#property
def plaatsen_beschikbaar(self):
geboekt = Aanmelden.objects.filter(activiteit_id=self.id).count()
return self.capaciteit - geboekt
def __str__(self):
return f"{self.id}:{self.titel}:{self.docent}"
I am working with an Angular UI with a Django-graphene GraphQL API and a postgres db.
Currently I have implemented a functionality to arrive at a global search field by adding a "searchField" for each table and updating it with each create and update of an item in that table. And using the graphql filter, every time a user would do a global search, I would just filter hte searchField for the query. I am very new to Django so I'm not sure if this is an efficient way to go about this, but this is what I have:-
Create mutation
class CreateUser(graphene.Mutation):
class Arguments:
input = UserInput(required=True)
ok = graphene.Boolean()
user = graphene.Field(UserType)
#staticmethod
def mutate(root, info, input=None):
ok = True
searchField = input.username if input.username is not None else "" + \
input.title if input.title is not None else "" + \
input.bio if input.bio is not None else ""
user_instance = User(user_id=input.user_id, title=input.title, bio=input.bio,
institution_id=input.institution_id, searchField=searchField)
user_instance.save()
return CreateUser(ok=ok, user=user_instance)
Update mutation
class UpdateUser(graphene.Mutation):
class Arguments:
id = graphene.Int(required=True)
input = UserInput(required=True)
ok = graphene.Boolean()
user = graphene.Field(UserType)
#staticmethod
def mutate(root, info, id, input=None):
ok = False
user = User.objects.get(pk=id)
user_instance = user
if user_instance:
ok = True
user_instance.name = input.name if input.name is not None else user.name
user_instance.avatar = input.avatar if input.avatar is not None else user.avatar
user_instance.institution_id = input.institution_id if input.institution_id is not None else user.institution_id
user_instance.title = input.title if input.title is not None else user.title
user_instance.bio = input.bio if input.bio is not None else user.bio
user_instance.searchField = user_instance.searchField + \
user_instance.name if user_instance.name is not None else ""
user_instance.searchField = user_instance.searchField + \
user_instance.title if user_instance.title is not None else ""
user_instance.searchField = user_instance.searchField + \
user_instance.bio if user_instance.bio is not None else ""
user_instance.save()
return UpdateUser(ok=ok, user=user_instance)
return UpdateUser(ok=ok, user=None)
Not sure if you can tell, but I'm very new to Python and Django. And what I'm doing here is, every time a user record is created or updated, I am maintaining a field in the table called searchField that will have a string of all the fields in the table that I want the global search to touch upon. I've manually written each line like that. Not sure if this is in line with best practice.
Here's the user model.
User model
class User(AbstractUser):
name = models.CharField(max_length=50)
email = models.EmailField(blank=False, max_length=255, unique=True)
avatar = models.CharField(max_length=250, blank=True, null=True)
institution = models.ForeignKey(
'Institution', on_delete=models.PROTECT, blank=True, null=True)
title = models.CharField(max_length=150, blank=True, null=True)
bio = models.CharField(max_length=300, blank=True, null=True)
searchField = models.CharField(max_length=600, blank=True, null=True)
USERNAME_FIELD = 'username'
EMAIL_FIELD = 'email'
So there are a couple of things here.
First I know that I'm doing something wrong in terms of setting up the updating of the searchField in the update mutation resolver method. That's due to my poor knowledge of Python. So if someone could sort out what I'm doing wrong that would be great.
I am doing this completely on my own, I have no idea if this approach is actually a good strategy in terms of efficiency or if there's already a good solution for graphql based django api. So if there is please point me to it.
Thank you.
Yes, if you want to update your searchField you have to build it from scratch. If you use old data and append new data, it will return the record if you search for old data and that's something you want to avoid.
Regarding your second question, you can use django-filter.
Steps:
install django-filter
create a base filterset class and add search field to it
class CustomFilterSet(filters.FilterSet):
search = filters.CharFilter(method='search_filter')
def search_filter(self, queryset, field_name, value):
return queryset
create filterset class for your models inheriting from your custom filterset class and override search_filter method. In order to perform or operations on querysets use Q object. In your case something like:
class UserFilterSet(CustomFilterSet):
...
def search_filter(self, queryset, field_name, value):
return queryset.filter(
Q(username__icontains=value) |
Q(title__icontains=vallue) |
Q(bio__icontains=value)
)
which looks for the value in specified fields regardless of
being lower/upper case
create graphql types and add related filterset_class to Meta class:
class UserType(DjangoObjectType):
class Meta:
model = User
filterset_class = UserFilterSet
interfaces = (relay.Node, )
Now you can search globally for the specified fields by passing just a single string.
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 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
On Django Forms, how do I specify a default value for a field if the user leaves it blank? Initial sets an initial value, but the user can delete this.
If you're using a ModelForm simply follow Dan's advice.
If however you're simply using a Form then you may have to specify how to deal with validation. Example Documentation
class YourForm(forms.Form):
...
def clean_field(self):
data = self.cleaned_data['field']
if not data:
data = 'default value'
return data
Set a default value in the model.
class YourModel(models.Model):
this_field = models.TextField(default="default_value")
WallyBay's answer works for me.
(+adding my experience)
If you leave the form field empty, None value will be passed.
you can check this by printing out from.cleaned_data().
But, in my case, None value wasn't replaced with the Model's default value.
I tested by creating objects in the shell('python manage.py shell')
passing 1)None value and 2)empty parameter.
My Model:
class Cart(models.Model):
total_amount = models.DecimalField(decimal_places=2, max_digits=1000, default=1, blank=True, null=True)
quantity = models.IntegerField(default=1, blank=True)
note = models.CharField(max_length=300, default='nothing to mention')
summery = models.TextField(blank=False, null=True)
event_won = models.BooleanField(default=False)
My Form:
class CartForm(forms.ModelForm):
summery = forms.CharField()
total_amount = forms.DecimalField(required=False)
quantity = forms.IntegerField(initial=20)
note = forms.CharField(widget=forms.TextInput(attrs={"placeholder":"write a note"}))
class Meta:
model = Cart
fields = [
'summery',
'total_amount',
'quantity',
'note'
]
1) create the object by passing None value.
Cart.objects.create(total_amount=None)
Result: Default value didn't apply. The total_amount is Null(None).
2) create the object without passing any.
Cart.objects.create()
Result: default value worked. total_amount is 1.
When I delete null=True option for the total_amount in the Model class, it gives me an error 'NOT NULL constraint failed'