grimboite/articles/dev/2013-05-23-breadcrumbs.md

3.6 KiB

Title Date Tags Slug
Ramasse-miettes 2013-05-23 python, sql, breadcrumb, dev breadcrumbs-folly

Pour faire suite à un premier article et toujours sur le même thème, voici une petite série d'idées d'implémentation pour mettre un ramasse-miettes en place.

En SQL, le plus simple est d'avoir une table qui a une référence vers elle-même. Un petit exemple ci-dessous en T-SQL:

CREATE TABLE Entity (
	[Id] int IDENTITY(1,1) NOT NULL PRIMARY KEY,
	[ParentId] int NULL FOREIGN KEY REFERENCES Entity(Id),
	[Label] nvarchar(255) NOT NULL
)

That's it. Par contre, cela ne donne quasiment aucune information, puisque pour construire l'arborescence sur cinq niveaux, il va falloir faire cinq requêtes distinctes: une première pour choper l'entité ayant un identifiant en particulier, ensuite choper son parent, puis choper le parent du parent, ... Jusqu'à arriver à la racine, pour laquelle le champ ParentId sera nul.

Une autre possibilité est de construire une fonction tabulaire qui, pour un identifiant donné, va construire la liste et la retourner dans une table temporaire, en y ajoutant un indice de position, la racine ayant la position zéro.

CREATE FUNCTION [dbo].[BreadCrumb]
(
	@currentid int
)
RETURNS @Tab TABLE (Id int, pos int, ParentId int, Label nvarchar(255))
	AS
Begin
	Declare @parentid int;
	Declare @label nvarchar(max);
	Declare @entitytypeid int;
	Declare @i int;

	Set @parentid = @currentid;
	Set @i = 0;

	While(@parentid is not null)
	Begin
		set @currentid = @parentid;

		(Select @parentid = ParentId, @label = Entity.Label
			From Entity Where Entity.Id = @currentid);

		INSERT INTO @Tab (id, pos, label, parentid)
		VALUES (@currentid, @i, @label, @parentid)
		set @i = @i + 1;
	End

	return;
END;

Comme expliqué ci-dessus, le résultat retourne une table qui contient quelques colonnes (l'id, le parentid, le label et la position), et permet de chipoter un peu parmi les liens de l'arborescence, puisqu'il suffit de filer un identifiant en paramètre à la fonction pour ressortir toute l'arborescence. Magique (ou presque).

Une autre approche est de passer par un ORM. Pour l'exemple (et parce que "c'est le plus mieux" ©), ça sera l'ORM de Django. Pour l'exemple, j'ai créé une simple classe 'Album', chaque album pouvant contenir plusieurs sous-albums. Le principe est le même: pour retracer l'arborescence d'un élément, il faut remonter jusqu'à la racine, afin de pouvoir tracer le ramasse-miettes.

class Album(models.Model):
	name = models.CharField(verbose_name='Nom', max_length=50)
	slug = models.SlugField(max_length=50, blank=False, editable=True)
	description = models.CharField(verbose_name='Description', max_length=255, null=True, blank=True)
	parent = models.ForeignKey('self', null=True, blank=True)
	created_at = models.DateTimeField(verbose_name='Date de création', auto_now_add=True)
	updated_at = models.DateTimeField(verbose_name='Date de modification', auto_now=True)

	def __unicode__(self):
		return self.name

	def illustration(self):
		""" returns an illustration for the album """
		pictures = self.picture_set.all()

		if len(pictures) > 0:
			return pictures[0].picture
		else:
			return None

	def parentbreadcrumb(self):
		return self.breadcrumb()[:-1]

	def breadcrumb(self):
		""" Returns the breadcrumb for the current album """
		if self.parent == None:
			return (self,)

		return self.parent.breadcrumb() + (self,)

	def path(self):
		"""
		returns the path where the album structure should be stored
		"""
		return os.sep.join((x.name for x in self.breadcrumb()))

(Avec quelques méthodes en plus pour faire joli).