Django Customize many to many query - django

I have extended Django's default User class by adding a ManyToManyField to another table called Algorithm. The new User class is:
class User(AbstractUser):
name = CharField(_("Name of User"), blank=True, max_length=255)
algorithms = ManyToManyField(Algorithm, blank=True, default=None, related_name="users")
def get_absolute_url(self):
return reverse("users:detail", kwargs={"username": self.username})
I want to specify if the user is_superuser or is_staff then User.algorithms.all() will get all algorithms. Otherwise, get only the records in the pivot table User_user_models. How can this be achieved? I tried to add a property method that check if superuser/staff then return all otherwise return super but it didn't work.
P.S: during creating/editing the user, if user is set to superuser or staff, then there is no need to choose algorithms.

I would just overwrite the save() method:
class User(AbstractUser):
name = CharField(_("Name of User"), blank=True, max_length=255)
algorithms = ManyToManyField(Algorithm, blank=True, default=None, related_name="users")
def get_absolute_url(self):
return reverse("users:detail", kwargs={"username": self.username})
def save(self, **kwargs):
if self.is_superuser or self.is_staff:
self.algorithms = Algorithm.objects.all()
else:
# Get your users from your pivot table
super().save()

Related

How to make a field dependant on another field inside one model in django

I would like the field joinkey pass the primary key of the project into the function random_int to generate a default joinkey for every new model being created. Is there a way to do this or am I approaching this problem the wrong way?
class Project(MainAbstractModel):
users = models.ManyToManyField(User)
title = models.CharField(max_length=25, default="")
joinkey = models.IntegerField(default=random_int(self.pk))
def other_user(self):
return self.users.exclude(username=user.username)
You can just use the #property decorator to handle that.
class Project(MainAbstractModel):
users = models.ManyToManyField(User)
title = models.CharField(max_length=25, default="")
#property
def joinkey(self):
return random_int(self.pk)
#joinkey.setter
def joinkey(self, value):
# handle setting a joinkey if need be
pass
def other_user(self):
return self.users.exclude(username=user.username)

While saving Django getting "This choice is not one of the available choices"

I am trying to save a form which have ForeignKey (purchaseContractID).Here is my contract Model
class contracts(models.Model):
productDetailID=models.ForeignKey('Inventory.productDetails',related_name='+',on_delete=models.CASCADE,verbose_name='Select Product',default=None)
supplierID=models.ForeignKey(suppliers,on_delete=models.CASCADE,verbose_name='Select Supplier',default=None)
totalUnits=models.IntegerField(verbose_name='Total Units',editable=False,default=None)
ratePerUnit=models.IntegerField(verbose_name='Rate Per Unit',default=None)
saleTax=models.IntegerField(verbose_name='Sale Tax',default=None)
incomeTax=models.IntegerField(verbose_name='Income Tax',default=None)
saleTaxwithHeld=models.IntegerField(verbose_name='Sale Tax with Held',default=None)
startDate=models.DateField(verbose_name='Start Date',default=None)
endDate=models.DateField(verbose_name='End Date',default=None)
manulContractNumber=models.IntegerField(verbose_name='Manul Contract Number',default=None)
paymentDays=models.IntegerField(verbose_name='Payment Days',default=None)
remarks=models.CharField(verbose_name='Remarks',max_length=100,default=None)
dateOfEntry=models.DateField(editable=False,default=datetime.now())
def __str__(self):
return str(self.productDetailID.name)
here is my inventoryIn Model which foreignKey of PurchaseContract
class inventoryIn(models.Model):
supplierID=models.ForeignKey('Purchase.suppliers',editable=False,on_delete=models.CASCADE,verbose_name='Supplier')
productID=models.ForeignKey(products,editable=False,on_delete=models.CASCADE)
purchaseContractID=models.ForeignKey('Purchase.contracts',on_delete=models.CASCADE,verbose_name='Contract ID')
unitsIn=models.IntegerField(verbose_name='Enter No of Bags')
MYCHOCIES = (('orginal', 'ORGINAL'), ('dummy', 'DUMMY'))
doType = models.CharField(blank=True, choices=MYCHOCIES, verbose_name='Select DO Type', max_length=20)
doID=models.IntegerField(verbose_name='Do No')
doImage=models.ImageField(upload_to='doImage/%Y/%m/%d',verbose_name='Do Image')
invoiceID=models.IntegerField(verbose_name='Invoice No')
invoiceImage=models.ImageField(upload_to='inventoryIn/%Y/%m/%d')
agingDate=models.DateField(verbose_name='Receiving Date')
labReportImage = models.ImageField(upload_to='labReportImage/%Y/%m/%d', blank=True,verbose_name='Lab Report Image')
enterPaymentDays = models.IntegerField(verbose_name='Enter Payment Days', blank=True, default=None)
dateOfEntry=models.DateField(default=datetime.now())
def __str__(self):
return self.supplierID
here is my admin.py where i am adding admin form and also adding a js that add some fields dynamical.
class inventoryInAdmin(admin.ModelAdmin):
fields = ['purchaseContractID','unitsIn','doType','doID','doImage','invoiceID','invoiceImage','agingDate','labReportImage','enterPaymentDays']
class Media:
js = ('js/addInventory.js',)
admin.site.register(inventoryIn,inventoryInAdmin)
it is not allowing me to submit form and giving me error "Select a valid choice. That choice is not one of the available choices."
I have resolved this issue by adding the save method at InventoryIn Model.
class inventoryIn(models.Model):
supplierID=models.ForeignKey('Purchase.suppliers',editable=False,on_delete=models.CASCADE,verbose_name='Supplier')
productID=models.ForeignKey(products,editable=False,on_delete=models.CASCADE)
purchaseContractID=models.ForeignKey('Purchase.contracts',on_delete=models.CASCADE,verbose_name='Contract ID')
unitsIn=models.IntegerField(verbose_name='Enter No of Bags')
MYCHOCIES = (('orginal', 'ORGINAL'), ('dummy', 'DUMMY'))
doType = models.CharField(blank=True, choices=MYCHOCIES, verbose_name='Select DO Type', max_length=20)
doID=models.IntegerField(verbose_name='Do No')
doImage=models.ImageField(upload_to='doImage/%Y/%m/%d',verbose_name='Do Image')
invoiceID=models.IntegerField(verbose_name='Invoice No')
invoiceImage=models.ImageField(upload_to='inventoryIn/%Y/%m/%d')
agingDate=models.DateField(verbose_name='Receiving Date')
labReportImage = models.ImageField(upload_to='labReportImage/%Y/%m/%d', blank=True,verbose_name='Lab Report Image')
enterPaymentDays = models.IntegerField(verbose_name='Enter Payment Days', blank=True, default=None)
dateOfEntry=models.DateField(default=datetime.now())
def __str__(self):
return str(self.supplierID)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
from Purchase.models import contracts,suppliers
contract = contracts.objects.values('supplierID', 'productDetailID').filter(id=self.purchaseContractID.id)
supplier=contract[0].get("supplierID")
product=contract[0].get("productDetailID")
self.supplierID=suppliers.objects.get(id=supplier)
self.productID=products.objects.get(productDetailsID=product)
super(inventoryIn,self).save()
and i also had made my two fields editable=False
supplierID=models.ForeignKey('Purchase.suppliers',editable=False,on_delete=models.CASCADE,verbose_name='Supplier')
productID=models.ForeignKey(products,editable=False,on_delete=models.CASCADE)

Django createview using primary key

I have a model like this:
class Appointment(models.Model):
user = models.ForeignKey(User)
engineer = models.ForeignKey(Engineer)
first_name = models.CharField(max_length=100)
middle_name = models.CharField(max_length=100, blank=True)
last_name = models.CharField(max_length=100)
age = models.IntegerField()
def __unicode__(self):
return self.first_name
I have created a form using this model and I want to save it. Here I want the 'engineer' field to to be as the primary key is passed in the url.. like
engineer = Engineer.objects.get(pk=pk)
How can I do this. Or I should create a normal form and get its value via get method and assign to the field??
Since this is a CreateView, you should build on the example in the documentation and add it in form_valid:
def form_valid(self, form):
form.instance.engineer = Engineer.objects.get(pk=self.kwargs['pk'])
return super(AppointmentCreate, self).form_valid(form)

Filtering choice fields in "real time" in Django Admin

I am having some issues filtering values in my choice fields.
Here is my model for my application:
from django.db import models
class Role(models.Model):
name = models.CharField(max_length=20, blank=False)
def __unicode__(self):
return self.name
class Rank(models.Model):
name = models.CharField(max_length=20, blank=False)
role = models.ForeignKey(Role, blank=False)
def __unicode__(self):
return self.name
class Member(models.Model):
name = models.CharField(max_length=100, blank=False)
rank = models.ForeignKey(Rank, verbose_name = 'Member Rank')
mainrole = models.ForeignKey(Role, related_name = 'main_role', blank=False)
secondaryrole = models.ForeignKey(Role, related_name = 'secondary_role', blank=False)
picture = models.ImageField(
null=True,
blank=False,
upload_to='member/pictures/',
)
def __unicode__(self):
return self.name
Just to clarify. A Member can have one Rank, and from that Rank, up to 2 different Roles. However, Ranks can have 'n' types of Roles.
Two issues I'm having:
In the admin, the Rank's name appears in the choice-field as many times as there are Roles attached to it. Is there a way for me to get rid of the repeated names and leave just one record of it? This might have something to do with how I modelled my classes, I am sort of new to Django.
If I choose a Rank, I need for the choice-fields of his two Roles to be filtered for the Roles available for that Rank, before the user even hits save (ajax-esque). Is there a way to do this? How?
Thank you for your time.
Editing with the code from my model admin:
class MemberInlineFormset(BaseInlineFormSet):
def add_fields(self, form, index):
super(MemberInlineFormset, self).add_fields(form, index)
roles = Role.objects.none()
if form.instance:
try:
rank = form.instance.rank
except Rank.DoesNotExist:
pass
else:
roles= Role.objects.filter(role__within=Rank.role)
form.fields['mainrole'].queryset = roles
form.fields['secondaryrole'].queryset = roles
class MemberInline(admin.TabularInline):
model = Member
formset = MemberInlineFormset

Django admin site for limited privilege users

I have a Django application, and have two apps within the project: accounts and store. In my store app, I have Product, Category model and other models.
I have admin site, from where I can register Products. In the accounts, I allow general people to signup and log in.
Now, I also want users to be able to register Products, so that they can sell their own products. However, I do not want to give them to a complete access to the admin site.
How can I give such limited access to the admin site to general users?
Thanks.
In admin you can make groups and assign access to models you wanted and same could be applied to users but you might be interested in limited access to models records to which logged user have itself added. in order to achieve this you have to define one column in model to foreign key to users like below I have defined model for company and assigned each company to user:
class Company(models.Model):
name = models.CharField(max_length=64, primary_key=True)
kam = models.ForeignKey(User, verbose_name='KAM', blank=True, null=True)
address = models.TextField(blank=True, null=True)
city = models.CharField(max_length=32, blank=True, null=True)
country = models.CharField(max_length=32, blank=True, null=True)
phone = models.CharField(max_length=32, blank=True, null=True)
fax = models.CharField(max_length=32, blank=True, null=True)
url = models.URLField(blank=True, null=True)
class Meta:
verbose_name_plural = 'Companies'
#unique_together = (('name', 'kam'),).
def __unicode__(self):
return self.name
Now your model will be associated with user, Now you could restrict records to be loaded according to logged user using admin.py in modeladmin definition like given below:
def queryset(self, request):
qs = super(CompanyAdmin, self).queryset(request)
# If super-user, show all comments
if request.user.is_superuser:
return qs
return qs.filter(kam=request.user)
thats simple let me know if this is what you want?
Also you could assign read only right. in model admin
Admin uses a class ModelAdmin to render the page as you would probably already know. That class has a queryset method which you override based with a new filter, based on who is accessing the site, as suggested by sharafjaffri.
But that filtering alone isn't sufficient. You also need to filter the values displayed in the dropdowns, to only those created by the user. And then when saved, you should associate the new object with the portal of the user adding it.
Here is my quick untested implementation of the same:
class PortalAdmin(admin.ModelAdmin):
exclude = ('portal',)
def queryset(self, request):
"""
Filter the objects displayed in the change_list to only
display those for the currently signed in user.
"""
qs = super(UserAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
else:
return qs.filter(portal=request.user.profile.portal)
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
the_model = db_field.related.parent_model
if hasattr(the_model,'portal'):
kwargs['queryset'] = the_model.objects.filter(portal=request.portal)
return super(PortalAdmin,self).formfield_for_foreignkey(db_field, request, **kwargs)
def save_model(self, request, obj, form, change):
if not change:
obj.portal = request.portal
obj.save()