Django - ManyToMany Field - Make a correct link - python

after an python3 manager.py inspectdb (mysql) and a look on few helps and tuto, i still have somes mistakes and incorrect results.
models.py
class A(models.Model):
ida = models.AutoField(db_column='idA', primary_key=True)
col1 = #an another column
has_B = models.ManyToManyField(B, related_name='a', through="AHasB", through_fields=('a_ida', 'b_idb'))
#I had add this line after a tuto in django book for the manytomayfield
class B(models.Model):
idb = models.AutoField(db_column='idB', primary_key=True)
col1 = #an another column
class AHasB(models.Model):
a_ida = models.ForeignKey(A)
b_idb = models.ForeignKey(B)
col1 = #an another column
view.py
def myview(request):
for element in b.filter(idb__in=a.values('has_B').distinct()):
print(element)
In my database i have,
A :
ida  | col1
1 | ...
2 | ...
3 | ...
B :
idb | col1
1 | ...
AHasB :
a_ida | b_idb
1 | 1
But when i will display result (ida -> idb) like a classic (SELECT idb,ida FROM A, B, AHasB WHERE AHasB.a_ida=A.ida AND AHasB.b_idb=B.idb), i have this ...
1 -> 1
2 -> 1
3 -> 1
And in normal case, i will just have 1 -> 1.
Maybe the model dont fit with my real database in back.
Edit
view.py
def myview(request):
a = A.objects.All()
b = B.objects.All()
for element_a in a.filter("somefilters"):
in_has_b = set(AHasB.objects.values_list('b_idb', flat=True));
print(b.filter(idb__in=in_has_b))

If you want to select all B instances that are referenced / owned by each A instance:
bs_for_each_a = {}
for a in A.objects.all():
bs = AHasB.objects.filter(a_ida=a).values('b_idb')
bs_for_each_a[a] = bs
If you need distinct Bs, you can try to add .distinct() after values('b_idb') but I did not test it. Another way to remove duplicates would be to use values_list('b_idb', flat=True) and pass the result of the query in set():
for a in A.objects.all():
bs = AHasB.objects.filter(a_ida=a).values_list('b_idb', flat=True)
bs_for_each_a[a] = set(bs)

Related

Union over fields having different names using peewee

I'm using peewee as ORM and have two classes like this:
class A(Model):
name = CharField()
body = TextField()
class B(Model):
title = CharField()
body = TextField()
I would like to get all entries from A and B whose title/name start with some characters like 'abc'. According to the documentation the | operator should help, but I'm not even able to execute the resulting Expression. Obviously I would like to have a UNION and AS expression behind the scenes. How do I get this via peewee?
You should be able to get the result you want with something like
result = (
A().select(A.name.alias('name_title'), A.body).where(A.name == 'abc') |
B().select(B.title.alias('name_title'), B.body).where(B.title == 'abc')
).select().execute()
or
search_text = 'abc'
table_a_results = A().select(
A.name.alias('name_title'),
A.body
).where(A.name == search_text)
table_b_results = B().select(
B.name.alias('name_title'),
B.body
).where(B.title == search_text)
result = ( table_a_results | table_b_results ).select().execute()
The .alias() method to gets you the AS functionality as per the docs

Django - Saving one form multiple times

I have a Django view that uses one form multiple times. The form is saving relationship Subgroup id as a foreign key and Student id as a foreign key .
The problem I'm having is when I try to save information to database it only saves the last record.
For example (database model):
1 858 | Pump | Iron
2 78 | Madagaskar| Thomas
And if Im trying to split them into seperate groups, only Madagaskar his data is saved:
id | timestamp | student_Id_id | subgroup_Id_id |
+----+----------------------------+---------------+----------------+
| 62 | 2016-05-06 10:54:49.022000 | 2 | 91 |
The form looks like this:
class ApplicationFormaFull1(MultiModelForm):
form_classes = {
'sub1': FormSubgroup,
'sub2': FormSubgroup,
'stud_sub': FormStudent_in_Subgroup
}
and my view :
sub = form['sub1'].save(commit=False)
sub.student_group = StudentGroup.objects.get(id=element)
sub.number = 1
sub.type = 'Other'
sub.student_count = firstSubgroup
sub.save()
sub1 = form['sub2'].save(commit=False)
sub1.student_group = StudentGroup.objects.get(id=element)
sub1.number = 2
sub1.type = 'Others'
sub1.student_count = secondSubgroup
sub1.save()
if (counter%2==1):
stud_sub = form['stud_sub'].save(commit=True)
stud_sub.subgroup_Id = sub
stud_sub.student_Id = Student.objects.get(id=student)
stud_sub.save()
else:
stud_sub = form['stud_sub'].save(commit=True)
stud_sub.subgroup_Id = sub1
stud_sub.student_Id = Student.objects.get(id=student)
stud_sub.save()
So to sum up, I want that every form would save its information multiple times (dynamically)
Maybe the solution is that I should store information in the list and after all forms are added, save them one by one ?
stud_sub = form['stud_sub'].save(commit=False)
stud_sub.subgroup_Id = sub
stud_sub.student_Id = Student.objects.get(id=student)
list.add(stud_sub)
...
for i in list:
i.save()
Other solution use formset:
ArticleFormSet = formset_factory(ArticleForm, extra=2)
formset = ArticleFormSet(initial=[
{'title': 'Django is now open source',
'pub_date': datetime.date.today(),}
])
However i dont know how to change title, pub_date and to add everyting to formset dynimically.

SQLAlchemy to join to same table on a column, but also need data from other columns in result

Assuming I have a model defined as so:
class DstEntry(Base):
__tablename__ = 'dst_entry'
id = Column('id', DBKeyType, primary_key=True)
name = Column(String(64))
ip_addr = Column(IPAddress)
logical_device_ip = Column(String(64))
And I have data like so:
id name ip_addr logical_device_ip
--------------------------------------------------
1 l3dst 3.3.3.3/32 192.168.99.151
2 httpdst 1.1.1.1/32 192.168.99.152 <===
3 httpdst2 2.2.2.0/24 192.168.99.151
4 dddd 1.1.1.1/32 192.168.99.153 <===
5 httpdst 1.1.1.1/32 192.168.99.151 <===
6 aadst 4.4.4.4/32 192.168.99.153
I would like to be able to perform a query to eventually get something that tell me:
ip_addr logical_device_ip
--------------------------------------------------
1.1.1.1/32 192.168.99.151, 192.168.99.152, 192.168.99.153
3.3.3.3/32 192.168.99.151
2.2.2.0/24 192.168.99.151
4.4.4.4/32 192.168.99.153
How do I do this with sqlalchemy?
Try this:
res = session.query(
DstEntry.ip_addr,
func.group_concat(DstEntry.logical_device_ip)
).group_by(DstEntry.ip_addr)
for r in res:
print r
Note:
logical_device_ip in the result are concatenated with , as separator. If you want to add a space character to the separator, do this instead
res = session.query(
DstEntry.ip_addr,
func.group_concat(DstEntry.logical_device_ip.op('SEPARATOR')(', '))
).group_by(DstEntry.ip_addr)
The raw SQL query for MySQL is:
SELECT ip_addr,
GROUP_CONCAT(logical_device_ip) AS logical_device_ip
FROM dst_entry
GROUP BY ip_addr;

Peewee and Database Inheritance

I'm trying to learn Peewee and Bottle by making a book note-taking application.
Say I have the following entities:
Subject
Book
Chapter
Note
Tag
I would like to be able to make Notes for Chapters, Books, and Subjects.
In a DB, you would do:
create table noteable (
noteable_id INT AUTO_INCREMENT PRIMARY KEY
,type VARCHAR(10) NOT NULL CHECK (type in ('SUBJECT','BOOK','CHAPTER','NOTE'))
);
create table subject (
subject_id INT AUTO_INCREMENT PRIMARY KEY
,noteable_id INT UNIQUE REFERENCES noteable (noteable_id)
,...
);
create table book (
book_id INT AUTO_INCREMENT PRIMARY KEY
,subject_id INT NOT NULL REFERENCES subject (subject_id)
,noteable_id INT UNIQUE REFERENCES noteable (noteable_id)
,...
);
create table chapter(
chapter_id INT AUTO_INCREMENT PRIMARY KEY
,book_id INT NOT NULL REFERENCES book (book_id)
,noteable_id INT UNIQUE REFERENCES noteable(noteable_id)
,...
);
create table note(
note_id INT AUTO_INCREMENT PRIMARY KEY
,noteable_id INT UNIQUE REFERENCES noteable(noteable_id)
,...
);
(If you wanted a M:N relationship between note and notable, you would do a note_notable bridge table as well).
You would have before insert triggers on subject, book, and chapter that would insert a row into noteable, retrieve the new row's noteable_id, and use that on the incoming row.
I'm assuming that if you are using an ORM like Peewee you would want to do that in application logic rather than triggers.
How can I implement this model in Peewee?
Here is how I did it. I couldn't find a native way in Peewee to implement inheritance so I just rolled it myslef. If there is a better way, please provide your answer and I'll award it.
import MySQLdb
import peewee
from peewee import *
from datetime import datetime
db = MySQLDatabase('test', user='root',passwd='psswd')
class BaseModel(Model):
class Meta:
database = db
class Noteable(BaseModel):
type = CharField(null = False)
# This will act as the trigger that inserts a row into noteable,
# and retrieves the notable.id to use
class N(BaseModel):
def save(self, *args, **kwargs):
if not self.id:
noteable = Noteable(type=self.__class__.__name__.upper())
noteable.save()
self.noteable = noteable.id
return super(N, self).save(*args, **kwargs)
class Subject(N):
name = CharField(null = False, unique = True)
noteable = ForeignKeyField(Noteable, related_name="noteablesubject", null= False, unique = True)
class Book(N):
name = CharField(null = False, unique = True)
subject = ForeignKeyField(Subject, related_name="books", null = False)
noteable = ForeignKeyField(Noteable, related_name="noteablebook", null= False, unique = True)
class Chapter(N):
name = CharField(null = False)
chapter_number = IntegerField(null = False)
book = ForeignKeyField(Book, related_name="chapters")
noteable = ForeignKeyField(Noteable, related_name="noteablechapter", null= False, unique = True)
class Note(BaseModel):
note = TextField(null = False)
# N.B. unique is not true, as multiple notes can go to the same subject/book/chapter
noteable = ForeignKeyField(Noteable, related_name="notes", null= False)
Note.drop_table(True)
Chapter.drop_table(True)
Book.drop_table(True)
Subject.drop_table(True)
Noteable.drop_table(True)
Noteable.create_table(True)
Subject.create_table(True)
Book.create_table(True)
Chapter.create_table(True)
Note.create_table(True)
s = Subject(name="subject")
s.save()
n = Note(note="subject notes", noteable = s.noteable)
n.save()
n = Note(note="subject notes 2", noteable = s.noteable)
n.save()
b = Book(name="book", subject=s)
b.save()
n = Note(note="book notes", noteable = b.noteable)
n.save()
n = Note(note="book notes 2", noteable = b.noteable)
n.save()
c = Chapter(chapter_number=1, name="chapter", book=b)
c.save()
n = Note(note="chapter notes", noteable=c.noteable)
n.save()
n = Note(note="chapter notes 2", noteable=c.noteable)
n.save()
(if you wished to have a many to many relationship between notes and notable, you would have to define a NoteNotable class with foreign keys and remove the FK from Note)
You can define a helper method to left join whichever class with notes:
def get_notes(clazz, id):
return clazz.select().join(Noteable).join(Note, JOIN_LEFT_OUTER).where(clazz.id = id)
You can iterate over it like:
% for note in chapter.noteable.notes:
% end
Here are the results from a SELECT * FROM NOTABLE;
+----+---------+
| id | type |
+----+---------+
| 1 | SUBJECT |
| 2 | BOOK |
| 3 | CHAPTER |
+----+---------+
Here are the results from a SELECT * FROM NOTE;
+----+-----------------+-------------+
| id | note | noteable_id |
+----+-----------------+-------------+
| 1 | subject notes | 1 |
| 2 | subject notes 2 | 1 |
| 3 | book notes | 2 |
| 4 | book notes 2 | 2 |
| 5 | chapter notes | 3 |
| 6 | chapter notes 2 | 3 |
+----+-----------------+-------------+

django make query

DB TABLE
select * from AAA;
id | Name | Class | Grade |
--------------------------------------
1 | john | 1 | A |
2 | Jane | 2 | B |
3 | Joon | 2 | A |
4 | Josh | 3 | C |
|
Code
Django
search_result = AAA.objects.filter(Grade = 'B').count()
print search_result
search_result -> 2
I want to change Grade to Class by VALUE.
Django
target_filter = 'Class'
search_result = AAA.objects.filter(__"target_filter..."__ = '3').count()
search_result -> 1
Q) How can I complete this code? Is it possible?
Maybe you can do it like this:
target_filter = 'Class'
filter_args = {target_filter: 3}
search_result = AAA.objects.filter(**filter_args).count()
you can shorten aeby's example by putting the kwargs in directly
target_filter = 'Class'
search_result = AAA.objects.filter(**{target_filter:3}).count()
It is somehow the same answer I gave here. You can also use the getattr but now with the module object itself. This is either __module__ if it is the same module, or the module you imported.
target_filter = 'Class'
search_result = AAA.objects.filter(getattr(__module__, target_filter) = '3').count()
EDIT: I got it wrong, it is not possible to access the current module via __module__. If the class is declared in the same module as your search, you can use globals()[target_filter] to access it. If your search is from another module, you can do it like this:
import somemodule
...
target_filter = 'Class'
search_result = AAA.objects.filter(getattr(somemodule, target_filter) = '3').count()

Categories