Deleting SQL data in associated OneToOneField model from admin - sql

I've got a model that references another model via OneToOneField so that when you use Django's built-in delete_selected admin action, the associated model's data is not deleted. I'd like to write a custom admin action to delete the data in that associated model as well.
Here's my model:
class Party(models.Model):
TYPE_CHOICES=(
('P','Person'),
('O','Organization')
)
partyType = models.CharField(max_length=1, choices=TYPE_CHOICES)
name = models.CharField(max_length=100)
comment = models.CharField(max_length=500,blank=True)
accessIdCrossRef=models.IntegerField(blank=True, null=True)
mailingLists = models.ManyToManyField(MailingList)
inMainList=models.BooleanField(default=False)
inSubList=models.BooleanField(default=False)
class Meta:
db_table='party'
ordering=['name',]
def __unicode__(self):
return self.name
class Person(models.Model):
party = models.OneToOneField(Party, editable=False)
firstName=models.CharField(max_length=60)
lastName=models.CharField(max_length=60)
...
def save(self):
if None == self.party :
print 'Creating party for person'
p = Party()
p.partyType = 'P'
p.save()
self.party = p
# Get address to set party name used in list
city=""
state=""
postalCode=""
try:
partyAddress = PartyPostalAddress.objects.get(party=self.party)
address = partyAddress.postalAddress
city=address.city
state=address.state
postalCode=address.postalCode
except PartyPostalAddress.DoesNotExist:
pass
self.party.name = '%s, %s - %s, %s %s' %(self.lastName, self.firstName, city, state, postalCode)
self.party.save()
super(Person,self).save()
My assumption was to write a def delete() in my model like this:
def delete(self):
self.party.delete()
self.delete()
And an admin action like so:
class PersonAdmin(admin.ModelAdmin):
list_display = ('lastName','firstName')
search_fields = ('firstName', 'lastName')
actions=['really_delete_selected']
def get_actions(self, request):
actions = super(PersonAdmin, self).get_actions(request)
del actions['delete_selected']
return actions
def really_delete_selected(self, request, queryset):
for obj in queryset:
obj.delete()
if queryset.count() == 1:
message_bit = "1 person was"
else:
message_bit = "%s people were" % queryset.count()
self.message_user(request, "%s successfully deleted." % message_bit)
really_delete_selected.short_description = "Delete selected entries"
That deletes person.party and most of person, but throws an error because person's party OneToOneField is now empty. The specific error is:
"AssertionError at /admin/common/person/
Party object can't be deleted because its id attribute is set to None."
Any ideas? This, this, and this question are related, but only one of them utilizes the OneToOneField and he did that erroneously.

I am getting a feeling that it should be as simple as switching the sequence of deleting the two (unless you have tried this already). Since the person is associated with party, once you delete person, you cannot access party. Hence you should do
person.party.delete()
person.delete()

Got it cleaned up and working!
My model:
class Party(models.Model):
name = models.CharField(max_length=100)
...
class Person(models.Model):
party = models.OneToOneField(Party, editable=False)
firstName=models.CharField(max_length=60)
lastName=models.CharField(max_length=60)
def delete(self):
d = self.party.id
Party.objects.get(id__exact=d).delete()
My admin:
class PersonAdmin(admin.ModelAdmin):
actions=['really_delete_selected']
def get_actions(self, request):
actions = super(PersonAdmin, self).get_actions(request)
del actions['delete_selected']
return actions
def really_delete_selected(self, request, queryset):
for obj in queryset:
obj.delete()
if queryset.count() == 1:
message_bit = "1 person was"
else:
message_bit = "%s people were" % queryset.count()
self.message_user(request, "%s successfully deleted." % message_bit)
really_delete_selected.short_description = "Delete selected entries"
...

Related

PyQt5 User accessing

We have a university system developed by pyqt5 and python, in the login interface, the instructor can log in by the id and pass, but after login, we have another interface to show the courses of the instructor based on the input id that was from previous interface, the problem that the interface of the courses does not show the courses for the instructor( the id is not accessible to next interface).
I noticed that each interface is not updated when we do action, what is the command to let interfaces' action linked together?
This is the code:
class InstructorLogin(QDialog):
def __init__(self):
super(InstructorLogin, self).__init__()
loadUi("NewInstructorLogin.ui",self)
self.LoginInst.clicked.connect(self.gotoAfterInstLogin)
self.ExitLoginInst.clicked.connect(self.gotoMainExit1)
self.PmuPassInstButton.setEchoMode(QtWidgets.QLineEdit.Password)
#For the password
def gotoAfterInstLogin(self):
global f
global user
user = self.PmuIDInstButton.text()
password = self.PmuPassInstButton.text()
# print(user)
if len(user)==0 or len(password)==0:
self.InvalidPassLab.setText("Please input all fields.")
else:
conn = sqlite3.connect("Cognito.db")
cur = conn.cursor()
query = 'SELECT Password FROM Instructor WHERE Instructor_id =\''+user+"\'"
cur.execute(query)
result_pass = cur.fetchone()[0] #to compare password
if result_pass == password:
self.gotoInstructorLoginAbulBashar()
#####end of noorsol
else:
self.InvalidPassLab.setText("Invalid username or password")
def gotoMainExit1(self):
widget.setCurrentIndex(widget.currentIndex()-1)
#Remove comment later
def gotoInstructorLoginAbulBashar(self):
widget.setCurrentIndex(widget.currentIndex()+15)
# def user_getter(self):
# return user
########################################################################################################################
class ChooseCourseInst(QMainWindow):
global user
def __init__(self):
super(ChooseCourseInst, self).__init__()
#self.afterObj = AfterInstructorLogin()
self.afterInstructorLogin = AfterInstructorLogin()
loadUi("CoursesChosenInt.ui",self)
widget.setCurrentIndex(widget.currentIndex()+4)
self.exitabulbashar.clicked.connect(self.gotoexitabulbashar)
#self.InstServButton.clicked.connect(self.gotoInstServButton)
self.coursesreq.activated.connect(self.gotoInstServButton)
#self.courses(user)
#print(user)
def courses(self):
self.instructorLogin = InstructorLogin()
conn = sqlite3.connect("Cognito.db")
query = 'SELECT DISTINCT Course_name FROM Course WHERE Instructor_id =\''+self.instructorLogin.gotoAfterInstLogin(user)+"\'"
cur = conn.cursor()
cur.execute(query)
final_result = [i[0] for i in cur.fetchall()]
for i in range(len(final_result)):
self.coursesreq.addItem(final_result[i])
def gotoexitabulbashar(self):
widget.setCurrentIndex(widget.currentIndex()-16)
def gotoInstServButton(self):
self.chosencourse = self.coursesreq.currentText()
self.afterInstructorLogin.labeltest.setText(self.chosencourse)
self.afterInstructorLogin.diplayInfo()
#self.afterInstructorLogin.labeltest.show()
widget.setCurrentIndex(widget.currentIndex()-11)
I tried to let the user id as global variable, and create object and call it from the previous class, it shows User not defined, but the i put it inside function and i call that function in another class, it is defined but the courses empty so i expect to know how to refresh interfaces to let the change in first interface reflected in the second

DRF update view with many to many field

Am trying to write update view,but got an error please help me to find the problem,thanks :)
At first I have many to many field in my model.It is my model
class Portfolio(models.Model):
name = models.CharField(max_length=50, unique=True, blank=False, null=True)
market = models.ForeignKey(Market, on_delete=models.DO_NOTHING, related_name='market')
investor = models.ForeignKey('accounts.User', on_delete=models.DO_NOTHING, related_name='investor')
assets = models.ManyToManyField(Assets, related_name='assets')
def __str__(self):
return self.name
After that I have a serializer for my view:
class PortfolioSerializer(serializers.ModelSerializer):
class Meta:
model = Portfolio
fields = ['name', 'market', 'investor', 'assets']
And it's my view:
class PortfolioUpdateView(APIView):
serializer_class = PortfolioSerializer
def put(self, request, *args,):
data = request.data
portfo = Portfolio.objects.get(id=id)
print(portfo)
serilize = self.serializer_class(instance=request.user, data=request.POST)
if serilize.is_valid():
name = serilize.data['name']
market = Market.objects.get(pk=int(request.POST.get('market', '')))
assets = Assets.objects.get(pk=int(request.POST.get('assets', '')))
Portfolio.objects.update(name=name, market=market,
assets=assets,
)
return portfo
else:
pass
and at the end it is my error:
TypeError at /market/update/1
put() got an unexpected keyword argument 'id'
I found the answer by my self,because I needed to use id for get obj so I used request.data that is body's data of object include obj's id and added query-set method for getting the class objs
class PortfolioUpdateView(viewsets.ModelViewSet):
serializer_class = PortfolioSerializer
def get_queryset(self):
portfolio = Portfolio.objects.all()
return portfolio
def put(self, request, *args, **kwargs):
data = Portfolio.objects.get(id=request.data['id'])
update_portfolio = Portfolio.objects.update(name=data['name']
, market=Market.objects.get(pk=int(request.POST.get('market', ''))))
update_portfolio.save()
for asset in data['assets']:
asset_obj = Assets.objects.update(asset_name=asset['asset_name'])
update_portfolio.assets.add(asset_obj)
serializer = PortfolioSerializer(update_portfolio)
return Response(serializer.data)
And this is the URL
router.register("update", PortfolioUpdateView, basename="update")

How to set permissions for a POST request in ModelViewSet's

How can I write my own permission class for POST requests when using ModelViewSet?
I already tried to write my own permission_classe with no success. Even if my permission class is returning false it is still granting access to the post request
models.py
class Building(models.Model, HitCountMixin):
user = models.ForeignKey(User, on_delete=models.CASCADE) limit_choices_to=Q(country=2921044) | Q(country=798544), on_delete=models.SET_NULL) #<------------ Eltern Element
name = models.CharField(max_length=200, null=True, blank=True)
description = models.TextField(max_length=2000,null=True, blank=True)
facilities = models.TextField(max_length=2000, null=True, blank=True)
...
views.py
class BuildingImageViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
serializer_class = BuildingImageSerializer
permission_classes = (permissions.IsAuthenticated, IsOwner,)
def get_queryset(self):
if self.request.user.is_authenticated:
return BuildingImage.objects.filter(building__user=self.request.user)
return None
permissions.py
class IsOwner(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
print("TEST")
return False
urls.py
router = routers.DefaultRouter()
router.register(r'buildingimages', myrest_views.BuildingImageViewSet, base_name="buildingimage")
If I I try to upload an image it is working, Why?
My IsOwner permission class is evaluated because I can see the print line with "TEST" in the console.
MY SOLUTION:
def has_permission(self, request, view):
if view.action == 'create':
building_url = request.POST.get('building')
building_path = urlparse(building_url).path
building_id = resolve(building_path).kwargs['pk']
building = Building.objects.get(id=building_id)
return building.user == request.user
return True
Pass list of classes, you used has_object_permission(), You need to write code inside has_permission() method.
permission_classes = [<class 'rest_framework.permissions.AllowAny'>]
you have to pass class that derive BasePermission class
permission.py
from rest_framework import permissions
class IsOwner(permissions.BasePermission):
def has_permission(self, request, view):
if <CONDITION>:
return True
else:
return False

Flask App Builder url_for views

I am struggling on how to connect URL_for links in my app.
I have a the basic skeleton app set up.
I wish to make a link to a function (that runs code) that is built in a view (MyView).
I essentially want to pass a variable (var1) to the view 'myview/method2/var1' and have that link showing in the ContactModelView.
Thanks
class MyView(BaseView):
route_base = "/myview"
#expose('/method2/<string:var1>')
#has_access
def fun_var(self, var1):
# go to function_file and run function with var1
param1, param2 = func_file.function(var1)
self.update_redirect()
return self.render_template('method3.html',
param1=param1,param2=param2, param3=prospects)
My models.py file has the following:
class Contact(Model):
id = Column(Integer, primary_key=True)
name = Column(String(150), unique = True, nullable=False)
var1 = Column(String(150))
contact_group_id = Column(Integer, ForeignKey('contact_group.id'))
contact_group = relationship("ContactGroup")
def prospect(self):
return Markup(
'prospect')
def __repr__(self):
return self.name
I then have in views:
class ContactModelView(ModelView):
datamodel = SQLAInterface(Contact)
label_columns = {'contact_group':'Contacts Group'}
list_columns = ['name','var1','contact_group', 'action']
show_fieldsets = [
(
'Summary',
{'fields':['name','var1','contact_group', 'prospect']}
)
]
In the documentation regarding BaseView, we see that
Its constructor will register your exposed urls on flask as a Blueprint
So make sure you add the view you created, in order to 'register the blueprint'. Use something like
appbuilder.add_view_no_menu(MyView())
You can run fabmanager list-views on the console to make sure your view was registered.
As your endpoint requires a var1 parameter, you have to provide that to url_for. Something like this will work:
class Contact(Model):
var1 = Column(String(150))
...
def prospect(self):
return Markup(
'prospect')

How do I print the actual contents of this list

The code is short and simple:
class Contact:
all_contacts = []
def __init__(self, name, email):
self.name = name
self.email = email
Contact.all_contacts.append(self)
c1 = Contact("Paul", "something#hotmail.com")
c2 = Contact("Darren", "another_thing#hotmail.com")
c3 = Contact("Jennie", "different#hotmail.com")
for i in Contact.all_contacts:
print(i)
Clearly all I want to do is print the 'all_contacts' list with the info I have added, but what I get is:
<__main__.Contact object at 0x2ccf70>
<__main__.Contact object at 0x2ccf90>
<__main__.Contact object at 0x2ccfd0>
What am I doing wrong?
Add the following to your Contact class:
class Contact:
...
def __str__(self):
return '%s <%s>' % (self.name, self.email)
This will tell Python how to render your object in a human-readable string representation.
Reference information for str
The __repr__ and __str__ methods for Contact aren't defined, so you get this default string representation instead.
def __str__(self):
return '<Contact %s, %s>' % (self.name, self.email)
Separate the container from the items stored in the container.
Add a __str__() method to Contact.
class Contact:
def __init__(self, name, email):
self.name = name
self.email = email
def __str__(self):
return "{} <{}>".format(self.name, self.email)
class ContactList(list):
def add_contact(self, *args):
self.append(Contact(*args))
c = ContactList()
c.add_contact("Paul", "something#hotmail.com")
c.add_contact("Darren", "another_thing#hotmail.com")
c.add_contact("Jennie", "different#hotmail.com")
for i in c:
print(i)