I have the following models.py
class FileIndex(models.Model):
filename = models.CharField(max_length=256)
filetype = models.CharField(max_length=16)
vendorid = models.IntegerField()
vendorname = models.CharField(max_length=256)
tablename = models.CharField(max_length=256)
class Meta:
db_table = 'file_index'
verbose_name = 'File/Vendor Index'
verbose_name_plural = 'File/Vendor Indicies'
def __str__(self):
return self.filename
class UserFile(models.Model):
userid_id = models.ManyToManyField(User)
fileid_id = models.ManyToManyField(FileIndex)
grant_date = models.DateTimeField()
revoke_date = models.DateTimeField(blank=True)
class Meta:
db_table = 'auth_files'
verbose_name = 'User File Matrix'
verbose_name_plural = 'User File Matricies'
def get_userids(self):
return "\n".join([u.pk for u in self.userid_id.all()])
def get_fileids(self):
return "\n".join([f.pk for f in self.fileindex.all()])
I then run the following view in my views.py -
class ExampleView(APIView):
model = cdx_composites_csv
serializer_class = cdx_compositesSerializer
def get(self, request, format=None):
if UserFile.objects.filter(fileid_id=1, userid_id=2).exists():
content = {
'status': 'Request Successful.'
}
return Response(content)
else:
content = {
'status': 'Request Failed.'
}
return Response(content)
It fails with the following error message -
"auth_files_fileid_id" is an index
The SQL it's trying to run is the following -
u'SELECT (1) AS "a" FROM "auth_files"
INNER JOIN "auth_files_fileid_id" ON ( "auth_files"."id" = "auth_files_fileid_id"."userfile_id" )
INNER JOIN "auth_files_userid_id" ON ( "auth_files"."id" = "auth_files_userid_id"."userfile_id" )
WHERE ("auth_files_fileid_id"."fileindex_id" = 1 AND "auth_files_userid_id"."user_id" = 2 ) LIMIT 1'
If I were to write the query in the DB this is how it would look - for some reason the query is waaaaaay off and I'm unsure why it's registering like above.
select * from auth_files af
inner join auth_user on au.id = af.userid
inner join fileindex fi on fi.id = af.fileid
where fi.id = 1 and au.id = 2
Looking at the query that Django is creating it's trying to inner join on itself I have no idea why.
This is ultimately what I want the query to be -
select fi.filename from auth_files af
inner join auth_user on au.id = af.userid
inner join fileindex fi on fi.id = af.fileid
where fi.filename = 'filename' and au.username = 'testuser'
If you want those queries, then you have the wrong relationship type (not to mention your extremely odd field names). You're describing a foreign key relationship, but you have declared a many-to-many. M2M relationships require a linking table, which Django automatically names with the source table + fieldname - but apparently this conflicts with an index you have defined.
I would change those very strange fieldnames into something more standard ("users" and "fileindexes" would do fine) and recreate the database.
Edited As confirmed by the comments, you do have the wrong field type here. UserFile is already the linking table of a many-to-many relationship, so you need to use ForeignKey fields to both User and File. Again, though, you should use sensible names: each UserFile can only relate to one of each, so these names should be "user" and "file".
The many-to-many relationship is really from FileIndex to User, going through FileIndex, so you should declare it like that.
class UserFile(models.Model):
user = models.ForeignKey(User)
file = models.ForeignKey(FileIndex)
...
class FileIndex(models.Model):
users = models.ManyToManyField(User, through=UserFile)
Related
I've got a database with an simple Employee model and node in Django. I´m using Graphene to create an API around this that allows a user to retrieve the right data.
class Employee(models.Model):
id = models.UUIDField(primary_key=True, unique=True, )
name = models.CharField(max_length=128)
class EmployeeNode(DjangoObjectType):
class Meta:
model = Employee
fields = "__all__"
interfaces = (graphene.relay.Node, )
Now in addition to this, I have a query that finds a "buddy" for every Employee, which is another Employee (ID) in the database, and a function (details irrelevant here) that finds the correct "buddy" in the database using some not further specified Django query.
class EmployeeNodeWithBuddy(DjangoObjectType):
buddy_id = graphene.UUID()
class Meta:
model = Employee
fields = "__all__"
interfaces = (graphene.relay.Node, )
#classmethod
def get_queryset(cls, queryset, info):
set_with_buddy_annotation = queryset.annotate(
buddy_id=ExpressionWrapper(Subquery(
### Omitting the details of this query ###
).only('id')[:1], output_field=models.UUIDField()
), output_field=models.UUIDField())
)
return set_with_buddy_annotation
This works ok, but what I actually want is not the ID of the buddy, but the actual EmployeeNode. I can´t figure out if there is a good way to annotate/add info to this query to make it return the thing I want. It would look like:
class EmployeeNodeWithBuddy(DjangoObjectType):
buddy_id = graphene.UUID()
buddy = graphene.Field(EmployeeNode) # Field with EmployeeNode instead of just ID!
class Meta:
model = Employee
fields = "__all__"
interfaces = (graphene.relay.Node, )
#classmethod
def get_queryset(cls, queryset, info):
set_with_buddy_annotation = queryset.annotate(
buddy_id=ExpressionWrapper(Subquery(
### Omitting the details of this query ###
).only('id')[:1], output_field=models.UUIDField()
), output_field=models.UUIDField())
)
set_with_buddy_annotation = # Somehow fetch the EmployeeNode from the buddy_id here?
return set_with_buddy_annotation
Does this make sense to do and is this even possible?
I'm making a little Django project of money management, I'm using a table layout, inside the tables there are many transactions. I have two SQL tables: "Table" and "Transactions" and I need that when I open the link of one specific table, I need to get just the items which were created in the table page.
Example:
I open 'table1' and inside it I create 'value1', 'value2','value4'
after, I open 'table2' and inside it I create 'value3' and 'value5'
after that, when I open the 'table1' page I need to show
'value1','value2' and 'value4'
and when I open 'table2', I need 'value3' and 'value5'
I wonder if there is a way to take the id of the table I'm inside in the moment and write it into the transactions form to make some kind of 'id', so I can filter the values by it id
Here are my files:
urls.py
from django.urls import path
import tables1.views as vw
urlpatterns = [
path('admin/', admin.site.urls, name = 'admin'),
path('mytables/', vw.mytables, name = 'mytables'),
path('',vw.home),
path('table/<int:pk>',vw.table, name = 'tableurl'),
path('newtable/',vw.newtable,name = 'newtable')
]
views.py
from .models import Table
from .forms import TableForm
def home(request):
now = {}
return render(request,'tables1/home.html',now)
def mytables(request):
data = {}
data['tables'] = Table.objects.all()
return render(request, 'tables1/mytables.html', data)
def table(request,pk):
form = TableForm(request.POST or None)
data = Table.objects.get(idd = pk)
print(data)
if form.is_valid():
form.save()
return redirect('mytables')
return render(request,'tables1/table.html',{'data':data, 'form':form}),pk
def newtable(request):
form = TableForm(request.POST or None)
if form.is_valid():
form.save()
return redirect('mytables')
return render(request,'tables1/newtable.html',{'form':form})
models.py
from .views import table
class Table(models.Model):
idd = models.AutoField(primary_key=True, default= None)
name = models.CharField(max_length=100)
date = models.DateField(auto_now_add=True)
time = models.TimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'Tables'
def __str__(self):
return self.name
class Transacao(models.Model):
mod = models
date = models.DateTimeField()
desc = models.CharField(max_length=200)
value = models.DecimalField(max_digits=7,decimal_places=2)
obs = models.TextField(null=True,blank=True)
class Meta:
verbose_name_plural = 'Transacoes'
def __str__(self):
return self.desc
forms.py
from .models import Table
from .models import Transacao
class TableForm(ModelForm):
class Meta:
model = Table
fields = ['name']
class TransacaoForm(ModelForm):
class Meta:
model = Transacao
fields = ['desc','date','value','obs'] ```
From my understanding of your question, you want a relation between Table and Transaction, where each Table contains (possibly 0 or more) transactions. This is a many-to-one relation that can be done by a Foreign Key. Read more.
I modified your code and added the foreign key to it. By adding the foreign key field name to the form you can set the table for each transaction when you create it. And when you need to get the transactions for a table you can do table.transacoes. The name transacoes is defined in the related-name attribute of the foreign key.
models.py
from .views import table
class Table(models.Model):
idd = models.AutoField(primary_key=True, default= None)
name = models.CharField(max_length=100)
date = models.DateField(auto_now_add=True)
time = models.TimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'Tables'
def __str__(self):
return self.name
class Transacao(models.Model):
# Forein key defined here
table = models.FogeinKey(Table, related_name='transacoes')
# Also I don't know what this is, you probably don't need it
mod = models
date = models.DateTimeField()
desc = models.CharField(max_length=200)
value = models.DecimalField(max_digits=7,decimal_places=2)
obs = models.TextField(null=True,blank=True)
class Meta:
verbose_name_plural = 'Transacoes'
def __str__(self):
return self.desc
forms.py
from .models import Table
from .models import Transacao
class TableForm(ModelForm):
class Meta:
model = Table
fields = ['name']
class TransacaoForm(ModelForm):
class Meta:
model = Transacao
# table added here
fields = ['desc', 'date', 'value', 'obs', 'table']
I managed to solve my problem by instead of saving the form data automatically in database with "form.save()", i """manually""" got the data from form using form.cleaned_data for each one of the form fields and saved that data into database using Transaction.objects.create() so that i could save the pk variable into the transactions table with the name of "tableid", that way, the app could link the primary key of each table page and link it to the transactions registered inside that page.
I am using select_nullable in a Django view to retrieve a relationship that can be null
nullable foreign keys must be specified
So I am explicitely passing it as a parameter:
source_text = get_object_or_404(Text.objects.select_related('language'), pk=source_text_pk)
The problem is that when I'm accessing it in a template it generates a database query, i.e.:
# items/templates/items/source.html
{{source.language.code}}
Testing it with:
# items/tests/test_views.py
...
source_text = TextFactory()
context = {'source': source_text}
with self.assertNumQueries(0):
# render the template with given context to trigger possible db hits
from django.template.loader import render_to_string
rendered = render_to_string("items/source.html", context)
Generates:
...
AssertionError: 1 != 0 : 1 queries executed, 0 expected
Captured queries were:
1. SELECT "languages_language"."id", "languages_language"."name", "languages_language"."code" FROM "languages_language" WHERE "languages_language"."id" = 16
Involved models are defined as:
# items/models.py
class Text(models.Model):
language = models.ForeignKey('languages.Language',
on_delete=models.SET_NULL,
blank=True,
null=True)
# languages/models.py
class Language(models.Model):
name = models.CharField(max_length=200)
code = models.CharField(max_length=35)
def __str__(self):
return "name: {} \tcode: {}".format(self.name, self.code)
How should I use select_related to not generate a database query in the view?
I'm trying to INNER join 2 tables: Comments and Auth. So I store userid in Comments table and I need to pair it with the Auth.
I did with .raw(), but I don't want to do with the raw(), I have tried also other like get_objects_or_404 but that does not work either because of multiple queries.
Here is my query, works as excepted.
SELECT * FROM index_comment INNER JOIN auth_user WHERE index_comment.userid=auth_user.id
And here is my Comment model:
class Comment(models.Model):
content = models.TextField()
userid = models.IntegerField()
published = models.DateField(default=timezone.now)
postid = models.IntegerField()
Views.py
def readPost(request, postName):
content = get_object_or_404(Post, link=postName)
kategori = get_object_or_404(Category, id=content.category_id)
user = get_object_or_404(User, id=content.username_id)
if request.method == "POST":
form = sendComment(request.POST)
if form.is_valid:
formContent = strip_tags(request.POST["content"])
newComment = Comment()
newComment.content = formContent
newComment.postid = content.id
newComment.save()
return redirect('post',content.link)
else:
form = sendComment
args = {
"content": content,
"kategori": kategori,
"user":user,
"commentForm": form,
}
# return HttpResponse("cat.id")
return render(request, "theme/single.html", args)
And here is the forms.py
class sendComment(forms.Form):
content = forms.CharField(widget=forms.TextInput(attrs={"class":"form-control"}))
So I need to pair userid from Comments table to Auth id, and then get the username.
By setting up your models properly with a foreign key relationship (django docs) from index_comment.userid to auth_user.id, Django will handle the join for you and make the columns of the joined table(auth_user) available through the primary model (index_comment).
Something like:
from django.db import models
from django.contrib.auth.models import User
class Comment(models.Model):
content = models.TextField()
userid = models.ForeignKey(User,on_delete=models.CASCADE)
published = models.DateField(default=timezone.now)
postid = models.IntegerField()
will tie the userid in your comment table to the user table of django's built auth functionality. the name fields will be available to Comment like
comment.userid.username
I have the following view -
class File_List(generics.ListAPIView):
model = cdx_composites_csv
serializer_class = cdx_compositesSerializer
def get_queryset(self):
"""
This view should return a list of all the purchases for
the user as determined by the username portion of the URL.
"""
filename = self.request.GET.get('filename')
model = get_model('markit', filename)
filedate = self.request.GET.get('filedate')
queryset = model.objects.using('markitdb').filter(Date__contains=filedate)
return queryset
For now ignore that model is listed twice it's the same model I need to figure that out later.
For permissions, I want it to deny access if the following occurs.
I have an extended Auth in my models.py
class FileIndex(models.Model):
filename = models.CharField(max_length=256)
filetype = models.CharField(max_length=16)
vendorid = models.IntegerField()
vendorname = models.CharField(max_length=256)
tablename = models.CharField(max_length=256)
class Meta:
db_table = 'file_index'
verbose_name = 'File/Vendor Index'
verbose_name_plural = 'File/Vendor Indicies'
def __str__(self):
return self.filename
class UserFile(models.Model):
userid = models.ForeignKey(User)
fileid = models.ForeignKey(FileIndex)
grant_date = models.DateTimeField()
revoke_date = models.DateTimeField(blank=True)
class Meta:
db_table = 'auth_files'
verbose_name = 'User File Matrix'
verbose_name_plural = 'User File Matricies'
I want to be able to say that if the filename does not show up in relation to the user access will be denied.
In SQL it's simply a matter of returning the list of filenames the user has access to -
select * from auth_files af
inner join auth_user au on au.id = af.userid
inner join file_index fi on fi.id = af.fileid
where au.user = 'user logged in'
then I want to tell it that if that if the filename in the request is in the list of filenames returned from the above query then yes the user can do what they want to do.
update - tweaking like so -
class File_List(generics.ListAPIView):
model = cdx_composites_csv
serializer_class = cdx_compositesSerializer
def get_queryset(self):
# authorized_files = UserFile.objects.filter(userid=self.request.user).values_list('fileid')
"""
This view should return a list of all the purchases for
the user as determined by the username portion of the URL.
"""
filename = self.request.GET.get('filename')
model = get_model('markit', filename)
filedate = self.request.GET.get('filedate')
if FileIndex.objects.filter(filename=filename).exists():
queryset = model.objects.using('markitdb').filter(Date__contains=filedate)
return queryset
I now get the following error -
'NoneType' object is not iterable
In your view you can check like this:
filename = self.request.GET.get('filename')
if FileIndex.objects.filter(filename=filename).exists():
#do something