I am adding functionality to my django application to add models(Lot) linked to another(Job) with an M2M field.
The problem:
I am using Django autocomplete light to add this model(Lot) in the form and i want this field to also be filtered by the input and another autocomplete field(Contract), this field is an object called contract that is in the reverse of the M2M field:
Relations:
{Lot}--reverse on the M2M--{Job}--Foreign Key--{Contract}
I am trying to filter lot in the autocomplete to only those where the contract key on the order matches a field in the form, i have looked through documentation and im unsure if there is anyway to do this, below is my latest attempt along with relevant code.
models.py(only fields for the relationship)
class Contract(Parent_model):
contract_id = models.IntegerField(null=False, default=0000)
class Job(Job_parent):
contract = models.ForeignKey(Contract, on_delete=models.CASCADE)
class Lot(Job_parent):
CO = models.ManyToManyField(Job, related_name='Lot')
autocomplete class based view(views.py)
class lot_auto_complete(autocomplete.Select2QuerySetView):
def get_queryset(self):
qs = Lot.objects.all()
contract_in = self.forwarded.get('contract', None)
if contract:
query_set = qs.filter(CO_set.first().contract=contract_in)
if self.q:
query_set = qs.filter(lot_number__icontains=self.q)
return query_set
Don't get me wrong I am not familiar with Django autocomplete light. But it seems that your code has slightly wrong. I think you can get what you want just by doing these:
# Just change your query
query_set = qs.objects.filter(CO__contract__contract_id__in=contract_in)
If contract_in list contain only id's. If it contains a list of the contract object then it will look like
query_set = qs.objects.filter(CO__contract__in=contract_in)
Related
I am just getting used to Django and I'm having trouble trying to filter data so that when I pass in a primary key, it uses that primary key to filter the data in a model so that only data courses associated with that primary key are listed. Unfortunately I keep getting 500 errors etc.
The models are:
class TrainingGroup(models.Model):
title = models.CharField(max_length=15)
img = models.CharField(max_length=50)
class TrainingCourse(models.Model):
title = models.CharField(max_length=30)
img = models.CharField(max_length=50)
group = models.ForeignKey(TrainingGroup, on_delete=models.CASCADE)
I am then trying to filter the TrainingCourse table/model down using the following view:
class TrainingView(View):
def get(self, request, **kwargs):
intPK = self.kwargs['pk']
group = TrainingGroup.objects.get(pk=intPK)
courses = TrainingCourse.objects.filter(pk__eq=intPK)
context = {
'group': group,
'courses': courses,
}
return render(request, 'training.html', context=context)
I must admit that I made the pk__eq up, but I'm not really sure what I am supposed to be using to filter this and I cannot get it to work.
p.s. The migration has created a table with the columns id, title and img.
Thanks
Mark
Instead of that you can get all related courses directly from group instance using reverse relation:
group = TrainingGroup.objects.get(pk=intPK)
courses = group.trainingcourse_set.all()
Note group's trainingcourse_set attribute. It's created automatically by django and allows you to fetch related objects in reverse way. You can find more details in the doc here and here.
As for your code, to make it work you can just use group_id field instead of pk__eq:
courses = TrainingCourse.objects.filter(group_id=intPK)
I'm using django_filters for an advanced search and select2Widget to display the options of a foreign key field.
The proper values load but whenever I submit the form I get an error message: Select a valid choice. That choice is not one of the available choices.
The error might seem pretty obvious but I can't find out how to solve it. Any suggestions?
filters.py
class MyFilter(django_filters.FilterSet):
b = django_filters.ModelChoiceFilter(
queryset=ModelA.objects.values_list('b__name', flat=True)
widget=Select2Widget()
)
class Meta:
model = ModelA
fields = ('b',)
models.py
class ModelA(models.Model):
b = models.ForeignKey('ModelB', on_delete=models.CASCADE)
class ModelB(models.Model):
name = models.CharField(max_length=100, unique=True)
def __str__(self):
return self.name
AS user #dirkgroten pointed out in a comment to the question, the following line looks strange:
queryset=ModelA.objects.values_list('b__name', flat=True)
This way the widget has no way of knowing the pk of each element of the list (since it only return the names). That might couse that the view cannot save a selected ModelB instance, since it does not know the selected pk.
Ah, you might also want to use ModelB instead of ModelA
Try changing it to something like this
queryset=ModelB.objects.values('pk', 'b__name')
or even this
queryset=ModelB.objects.all()
and let us know if that works.
I have a a model Meeting that is related to Signup which in turn is related Attendee.
Models:
class Meeting(models.Model):
date = models.DateTimeField(auto_now=False)
class Signup(models.Model):
meeting = models.ForeignKey(Meeting, on_delete=models.CASCADE, default=1)
...
class Attendee(models.Model, PrintNameTag):
signup = models.ForeignKey(Signup, on_delete=models.CASCADE, related_name="attendee")
attendee = models.CharField(blank=False, max_length=200)
...
How do I make a queryset on Meeting.objects that will also return the other fields in Signup and Attendee? I need all data for a report. I'd prefer not to change my models because this seems like this should be doable the way they are. I'd also like to query Meeting because I have some other stuff in the query that Meeting is needed for (removed for simplicity).
Here's what I currently have:
qs = Meeting.objects.filter(id__in=queryset).prefetch_related('signup').annotate(attendee_signup=F('signup__attendee_signup'))
I keep getting "cannot resolve keyword attendee_signup into field"
I also have tried variations of:
qs = Meeting.objects.filter(id__in=queryset).select_related('signup').filter(attendee__signup=signup_id)
I get "name 'signup_id' is not defined"
I'm brand new to python and Django and I don't know if I need to use F and/or annotate, prefech_related or select_related. I've looked up what each does, but I can't find the right syntax to get this working. I feel like I've tried every combo and I'm just mashing keys at this point. Could someone help please?
If you add related_name='signups' to the Signup.meeting field, then this should work:
meetings = Meeting.objects.filter(id__in=queryset).prefetch_related('signups__attendee')
The above will fetch all meetings, and all signups for those meetings, and all attendees for those signups. From there you can iterate over everything and it will all have already been fetched from the database:
for m in meetings:
for s in m.signups:
for a in s.attendee:
# whatever
(Incidentally, I would recommend changing that related_name from "attendee" to "attendees" as the plural form tends to be more intuitive.)
I need to implement the following:
The user shall be presented with a form that will have a drop down choice menu consisting of property names. There are two types of properties: general properties, i.e. properties common for all users and custom properties, i.e. properties that each user has defined prior to that. The models would look something like that:
class GeneralPropertyName(models.Model):
name = models.CharField(max_length=20)
class CustomPropertyName(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=20)
The drop down menu should have all general properties and only those custom properties that pertain to the user.
First question: how to define such a model?
I need to: 1. somehow unify both properties, 2. take only those items from CustomPropertyName that pertain to the user
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
property = ForeignKey(GeneralPropertyName) ??UNIFY??? ForeignKey(CustomPropertyName)
Second, is there anything special that needs to be done with ModelForm?
class SpecDataForm(ModelForm):
class Meta:
model = SpecData
And the 3rd question is what needs to be done in the view? I will need to use inline formsets since I will have a few dynamic forms like that.
def index(request):
user = User.objects.get(username=request.user.username)
specdataFormSet = inlineformset_factory(User, SpecData, form=SpecDataForm, extra=30)
...
specdata_formset = specdataFormSet(instance=user, prefix='specdata_set')
...
Thanks.
EDIT: Adjusted juliocesar's suggestion to include formsets. Somehow I am getting the following error message: Cannot resolve keyword 'property' into field. Choices are: id, name, selection_title, user
def index(request):
user = User.objects.get(username=request.user.username)
user_specdata_form = UserSpecDataForm(user=user)
SpecdataFormSet = inlineformset_factory(User, SpecData, form=user_specdata_form, extra=30)
You can use a GenericForeignKey to handle it, but you still need more to solve your further questions about forms and view.
I have made an example of how you solve your problem (logged user can select from General properties and his Custom properties, non-logged user only can select General properties). I used model inheritance for the properties (In your sample code it seems that a CustomPropertyName is a PropertyName with other fields). I think inheritance is an easier and a more basic concept than ContentTypes and it fits to your needs.
NOTE: I remove some code like imports to simplify the code.
1) models.py file:
class PropertyName(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
class CustomPropertyName(PropertyName): # <-- Inheritance!!
user = models.ForeignKey(User)
def __unicode__(self):
return self.name
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
property = models.ForeignKey(PropertyName)
NOTES: The field SpecData.property points to PropertyName since all properties are saved in the PropertyName's database table.
2) forms.py file:
from django import forms
from django.db.models import Q
from models import SpecData, PropertyName
def UserSpecDataForm(user=None):
UserPropertiesQueryset = PropertyName.objects.filter(Q(custompropertyname__user=None) | Q(custompropertyname__user__id=user.id))
class SpecDataForm(forms.ModelForm):
property = forms.ModelChoiceField(queryset=UserPropertiesQueryset)
class Meta:
model = SpecData
exclude = ('user',)
return SpecDataForm
NOTES: The trick here is to generate the form SpecDataForm dynamically, by filtering properties according the user specified in the parameter.
3) views.py file:
from forms import UserSpecDataForm
def index(request):
if request.POST:
form = UserSpecDataForm(request.user)(request.POST) # instance=user
if form.is_valid():
spec_data = form.save(commit=False)
spec_data.user = request.user
spec_data.save()
else:
form = UserSpecDataForm(request.user)()
return render_to_response('properties.html', {'form': form}, context_instance=RequestContext(request))
NOTES: Nothing special here, just a call to form.UserSpecDataForm(request.user) that returns the form class and then instantiate. Also setted the logged-in user to the object returned on save since It was excluded in the form to not show in front-end.
Following this basic example you can do the same with formsets if you need it.
UPDATE:
Formset can be used by adding following code to the view:
user_specdata_form = UserSpecDataForm(user=request.user)
SpecdataFormSet = inlineformset_factory(User, SpecData, form=user_specdata_form, extra=30)
The complete project sample can be downloaded from http://ge.tt/904Wg7O1/v/0
Hope this helps
1a) have you looked into django's ContentType framework this will allow you to have generic foreign keys and you can put restrictions on what types of models are acceptable to store in.
1b) I think that the validation for accepting what type of foreign key is acceptable shouldn't be in your model but should be part of your form validation before saving.
2) If you do use a model form you're going to have to define your own custom widget for the propery field. This means you're probably going to have to write you're own render function to render the html from the field. You should also define your own validation function on the form to make sure that only the appropriate data is acceptable to save.
3) I don't think you'll have to do anything you aren't already doing in the views
Use GenericForeignKey:
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
property = GenericForeignKey('content_type', 'object_id')
You can use this to combine the two fields(type & id) into a single choice field.
One way is that you have only one model, make user nullable:
class PropertyName(models.Model):
user = models.ForeignKey(User, null=True, blank=True)
name = models.CharField(max_length=20)
class SpecData(models.Model):
user = models.ForeignKey(User)
selection_title = models.CharField(max_length=20)
property = ForeignKey(PropertyName)
So, if user is not set, it is a general property. If it is set, it is related to this user.
However, please note that if you need unique property names, that NULL != NULL.
Of course, the suggested GenericForeignKey solution is better for some cases.
Also, you can easily make the normal (non-model) form with that you describe and separate form logic from model logic.
I have a form which uses multiple models, which have relationships. If the data for one of the forms matches an existing entry, I want to have the foreign key map to the existing entry rather than create a duplicate entry. However, is_valid() fails for the already existing entry and I'm not sure if it is safe to use the form data before it has been cleaned to do a database lookup for existence first. Perhaps I need to change the clean function to ignore the uniqueness requirement and then handle it in the view?
Here's an example, a user enters their name, city and state in a web form. If that city is already known to the database, then the foreign key for the person should just point to the existing entry. If it is a new city, it should be added to the database. So it is always a CREATE for Person, but it may or may not be a CREATE for Hometown.
models.py
class Person(models.Model):
name = models.CharField()
hometown = models.ForeignKey('Hometown')
class Hometown(models.Model):
cityName = models.CharField()
stateName = models.CharField()
mascot = models.CharField()
#If same city and state, it's the same place
class Meta:
unique_together = ("cityName", "stateName")
forms.py
class PersonForm(ModelForm):
class Meta:
model = Person
exclude = ('hometown')
class HometownForm(ModelForm):
class Meta:
model = Hometown
views.py
def newPerson(request):
if request.method == 'POST':
person = PersonForm(request.POST)
hometown = HometownForm(request.POST)
if (person.is_valid() and hometown.is_valid():
p = person.save(commit=False)
h = Hometown.objects.get_or_create(**hometown.cleaned_data)
p.hometown = h
p.save()
This code doesn't work, because hometown.is_valid() will be False if that city/state pair is already in the database. Should I override the clean() function to allow ignore the uniqueness requirement (enforcing it in the view by using get_or_create) or is that an indication that my design is fundamentally the wrong way of addressing this problem?
A similar question on stackoverflow contained the information I needed. Free-form input for ForeignKey Field on a Django ModelForm
As applied to my original question, I removed the HometownForm and instead added the relevant fields to PersonForm, then made the save() function of PersonForm handle the get_or_create() behavior for the Hometown.