1
+ """
2
+ This script transforms a Django model with a ManyToManyField into an intermediary model
3
+ with a ManyToManyField that uses a through model. It also generates the SQL query
4
+ for creating the intermediary table.
5
+
6
+ NOTE: This script assumes the original code is well-formed and follows Django's conventions.
7
+ NOTE: The script does not handle all edge cases and is meant to create a skeleton for the actual code.
8
+
9
+ To use this script, modify the `original_code` variable with your Django model code and run the script.
10
+ """
11
+ import re
12
+
13
+
14
+ def generate_intermediary_code (original_code ):
15
+ # Extract class name and many-to-many field definition
16
+ class_match = re .search (r'class (\w+)\((?:models\.Model|[\w\.]+)\):' , original_code )
17
+ m2m_match = re .search (r'(\w+)\s*=\s*models\.ManyToManyField\(\s*"?(self|\w+)"?\s*(.*)\)' , original_code )
18
+
19
+ if not class_match or not m2m_match :
20
+ return "Invalid original code format"
21
+
22
+ class_name = class_match .group (1 )
23
+ field_name = m2m_match .group (1 )
24
+ related_model = m2m_match .group (2 )
25
+ additional_args = m2m_match .group (3 ).strip ()
26
+
27
+ # Handle self-referencing models
28
+ if related_model == "self" :
29
+ related_model = class_name
30
+ intermediary_model_name = f"{ class_name } Friend"
31
+ from_field_name = f"from_{ class_name .lower ()} "
32
+ to_field_name = f"to_{ class_name .lower ()} "
33
+ else :
34
+ intermediary_model_name = f"{ class_name } { related_model } "
35
+ from_field_name = class_name .lower ()
36
+ to_field_name = related_model .lower ()
37
+
38
+ # Properly format the new code with a ManyToManyField that uses through
39
+ additional_args_str = f", { additional_args } " if additional_args else ""
40
+ through_str = f'through="{ intermediary_model_name } "'
41
+ new_code = re .sub (
42
+ r'(\s*' + field_name + r'\s*=\s*models\.ManyToManyField\([^\)]*\))' ,
43
+ rf'\n { field_name } = models.ManyToManyField("{ related_model } "{ additional_args_str } , { through_str } )' ,
44
+ original_code
45
+ ).replace (",," , "," ).replace (", ," , "," ) # This will clean up any double commas
46
+
47
+ intermediary_code = f"""
48
+
49
+ class { intermediary_model_name } (models.Model):
50
+ { from_field_name } = models.ForeignKey({ class_name } , on_delete=models.CASCADE)
51
+ { to_field_name } = models.ForeignKey({ related_model } , on_delete=models.CASCADE)
52
+
53
+ class Meta:
54
+ unique_together = (('{ from_field_name } ', '{ to_field_name } '),)
55
+ db_table = "{ class_name .lower ()} _{ related_model .lower ()} "
56
+ """
57
+
58
+ # Generate the SQL query for creating the intermediary table
59
+ sql_query = f"""
60
+ CREATE TABLE `{ class_name .lower ()} _{ related_model .lower ()} ` (
61
+ `{ from_field_name } _id` BIGINT NOT NULL,
62
+ `{ to_field_name } _id` BIGINT NOT NULL,
63
+ SHARD KEY (`{ from_field_name } _id`),
64
+ UNIQUE KEY (`{ from_field_name } _id`, `{ to_field_name } _id`),
65
+ KEY (`{ from_field_name } _id`),
66
+ KEY (`{ to_field_name } _id`)
67
+ );
68
+ """
69
+
70
+ return new_code + intermediary_code + "\n SQL Query:\n " + sql_query
71
+
72
+
73
+ original_code = """
74
+ class Book(models.Model):
75
+ name = models.CharField(max_length=100)
76
+ authors = models.ManyToManyField(Author, related_name="books")
77
+ publisher = models.ForeignKey(
78
+ Publisher,
79
+ models.CASCADE,
80
+ related_name="books",
81
+ db_column="publisher_id_column",
82
+ )
83
+ updated = models.DateTimeField(auto_now=True)
84
+ """
85
+
86
+ new_code = generate_intermediary_code (original_code )
87
+ print (new_code )
0 commit comments